The new filedialog stuff does not handle template. So do not call if
[wine] / files / drive.c
1 /*
2  * DOS drives handling functions
3  *
4  * Copyright 1993 Erik Bos
5  * Copyright 1996 Alexandre Julliard
6  */
7
8 #include "config.h"
9
10 #include <assert.h>
11 #include <ctype.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #include <errno.h>
18 #include <unistd.h>
19
20 #ifdef HAVE_SYS_PARAM_H
21 # include <sys/param.h>
22 #endif
23 #ifdef STATFS_DEFINED_BY_SYS_VFS
24 # include <sys/vfs.h>
25 #else
26 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
27 #  include <sys/mount.h>
28 # else
29 #  ifdef STATFS_DEFINED_BY_SYS_STATFS
30 #   include <sys/statfs.h>
31 #  endif
32 # endif
33 #endif
34
35 #include "winbase.h"
36 #include "wine/winbase16.h"   /* for GetCurrentTask */
37 #include "wine/winestring.h"  /* for lstrcpyAtoW */
38 #include "winerror.h"
39 #include "drive.h"
40 #include "file.h"
41 #include "heap.h"
42 #include "msdos.h"
43 #include "options.h"
44 #include "wine/port.h"
45 #include "task.h"
46 #include "debugtools.h"
47
48 DEFAULT_DEBUG_CHANNEL(dosfs)
49 DECLARE_DEBUG_CHANNEL(file)
50
51 typedef struct
52 {
53     char     *root;      /* root dir in Unix format without trailing / */
54     char     *dos_cwd;   /* cwd in DOS format without leading or trailing \ */
55     char     *unix_cwd;  /* cwd in Unix format without leading or trailing / */
56     char     *device;    /* raw device path */
57     char      label[12]; /* drive label */
58     DWORD     serial;    /* drive serial number */
59     DRIVETYPE type;      /* drive type */
60     UINT    flags;     /* drive flags */
61     dev_t     dev;       /* unix device number */
62     ino_t     ino;       /* unix inode number */
63 } DOSDRIVE;
64
65
66 static const char * const DRIVE_Types[] =
67 {
68     "floppy",   /* TYPE_FLOPPY */
69     "hd",       /* TYPE_HD */
70     "cdrom",    /* TYPE_CDROM */
71     "network"   /* TYPE_NETWORK */
72 };
73
74
75 /* Known filesystem types */
76
77 typedef struct
78 {
79     const char *name;
80     UINT      flags;
81 } FS_DESCR;
82
83 static const FS_DESCR DRIVE_Filesystems[] =
84 {
85     { "unix",   DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
86     { "msdos",  DRIVE_SHORT_NAMES },
87     { "dos",    DRIVE_SHORT_NAMES },
88     { "fat",    DRIVE_SHORT_NAMES },
89     { "vfat",   DRIVE_CASE_PRESERVING },
90     { "win95",  DRIVE_CASE_PRESERVING },
91     { NULL, 0 }
92 };
93
94
95 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
96 static int DRIVE_CurDrive = -1;
97
98 static HTASK16 DRIVE_LastTask = 0;
99
100
101 /***********************************************************************
102  *           DRIVE_GetDriveType
103  */
104 static DRIVETYPE DRIVE_GetDriveType( const char *name )
105 {
106     char buffer[20];
107     int i;
108
109     PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
110     for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
111     {
112         if (!strcasecmp( buffer, DRIVE_Types[i] )) return (DRIVETYPE)i;
113     }
114     MESSAGE("%s: unknown type '%s', defaulting to 'hd'.\n", name, buffer );
115     return TYPE_HD;
116 }
117
118
119 /***********************************************************************
120  *           DRIVE_GetFSFlags
121  */
122 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
123 {
124     const FS_DESCR *descr;
125
126     for (descr = DRIVE_Filesystems; descr->name; descr++)
127         if (!strcasecmp( value, descr->name )) return descr->flags;
128     MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
129         name, value );
130     return DRIVE_CASE_PRESERVING;
131 }
132
133
134 /***********************************************************************
135  *           DRIVE_Init
136  */
137 int DRIVE_Init(void)
138 {
139     int i, len, count = 0;
140     char name[] = "Drive A";
141     char path[MAX_PATHNAME_LEN];
142     char buffer[80];
143     struct stat drive_stat_buffer;
144     char *p;
145     DOSDRIVE *drive;
146
147     for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
148     {
149         PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
150         if (path[0])
151         {
152             p = path + strlen(path) - 1;
153             while ((p > path) && ((*p == '/') || (*p == '\\'))) *p-- = '\0';
154             if (!path[0]) strcpy( path, "/" );
155
156             if (stat( path, &drive_stat_buffer ))
157             {
158                 MESSAGE("Could not stat %s, ignoring drive %c:\n", path, 'A' + i );
159                 continue;
160             }
161             if (!S_ISDIR(drive_stat_buffer.st_mode))
162             {
163                 MESSAGE("%s is not a directory, ignoring drive %c:\n",
164                     path, 'A' + i );
165                 continue;
166             }
167
168             drive->root = HEAP_strdupA( SystemHeap, 0, path );
169             drive->dos_cwd  = HEAP_strdupA( SystemHeap, 0, "" );
170             drive->unix_cwd = HEAP_strdupA( SystemHeap, 0, "" );
171             drive->type     = DRIVE_GetDriveType( name );
172             drive->device   = NULL;
173             drive->flags    = 0;
174             drive->dev      = drive_stat_buffer.st_dev;
175             drive->ino      = drive_stat_buffer.st_ino;
176
177             /* Get the drive label */
178             PROFILE_GetWineIniString( name, "Label", name, drive->label, 12 );
179             if ((len = strlen(drive->label)) < 11)
180             {
181                 /* Pad label with spaces */
182                 memset( drive->label + len, ' ', 11 - len );
183                 drive->label[12] = '\0';
184             }
185
186             /* Get the serial number */
187             PROFILE_GetWineIniString( name, "Serial", "12345678",
188                                       buffer, sizeof(buffer) );
189             drive->serial = strtoul( buffer, NULL, 16 );
190
191             /* Get the filesystem type */
192             PROFILE_GetWineIniString( name, "Filesystem", "win95",
193                                       buffer, sizeof(buffer) );
194             drive->flags = DRIVE_GetFSFlags( name, buffer );
195
196             /* Get the device */
197             PROFILE_GetWineIniString( name, "Device", "",
198                                       buffer, sizeof(buffer) );
199             if (buffer[0])
200                 drive->device = HEAP_strdupA( SystemHeap, 0, buffer );
201
202             /* Make the first hard disk the current drive */
203             if ((DRIVE_CurDrive == -1) && (drive->type == TYPE_HD))
204                 DRIVE_CurDrive = i;
205
206             count++;
207             TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
208                   "flags=%08x dev=%x ino=%x\n",
209                   name, path, DRIVE_Types[drive->type],
210                   drive->label, drive->serial, drive->flags,
211                   (int)drive->dev, (int)drive->ino );
212         }
213         else WARN("%s: not defined\n", name );
214     }
215
216     if (!count) 
217     {
218         MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
219         /* Create a C drive pointing to Unix root dir */
220         DOSDrives[2].root     = HEAP_strdupA( SystemHeap, 0, "/" );
221         DOSDrives[2].dos_cwd  = HEAP_strdupA( SystemHeap, 0, "" );
222         DOSDrives[2].unix_cwd = HEAP_strdupA( SystemHeap, 0, "" );
223         strcpy( DOSDrives[2].label, "Drive C    " );
224         DOSDrives[2].serial   = 0x12345678;
225         DOSDrives[2].type     = TYPE_HD;
226         DOSDrives[2].flags    = 0;
227         DRIVE_CurDrive = 2;
228     }
229
230     /* Make sure the current drive is valid */
231     if (DRIVE_CurDrive == -1)
232     {
233         for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
234         {
235             if (drive->root && !(drive->flags & DRIVE_DISABLED))
236             {
237                 DRIVE_CurDrive = i;
238                 break;
239             }
240         }
241     }
242
243     return 1;
244 }
245
246
247 /***********************************************************************
248  *           DRIVE_IsValid
249  */
250 int DRIVE_IsValid( int drive )
251 {
252     if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
253     return (DOSDrives[drive].root &&
254             !(DOSDrives[drive].flags & DRIVE_DISABLED));
255 }
256
257
258 /***********************************************************************
259  *           DRIVE_GetCurrentDrive
260  */
261 int DRIVE_GetCurrentDrive(void)
262 {
263     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
264     if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
265     return DRIVE_CurDrive;
266 }
267
268
269 /***********************************************************************
270  *           DRIVE_SetCurrentDrive
271  */
272 int DRIVE_SetCurrentDrive( int drive )
273 {
274     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
275     if (!DRIVE_IsValid( drive ))
276     {
277         SetLastError( ERROR_INVALID_DRIVE );
278         return 0;
279     }
280     TRACE("%c:\n", 'A' + drive );
281     DRIVE_CurDrive = drive;
282     if (pTask) pTask->curdrive = drive | 0x80;
283     return 1;
284 }
285
286
287 /***********************************************************************
288  *           DRIVE_FindDriveRoot
289  *
290  * Find a drive for which the root matches the beginning of the given path.
291  * This can be used to translate a Unix path into a drive + DOS path.
292  * Return value is the drive, or -1 on error. On success, path is modified
293  * to point to the beginning of the DOS path.
294  */
295 int DRIVE_FindDriveRoot( const char **path )
296 {
297     /* idea: check at all '/' positions.
298      * If the device and inode of that path is identical with the
299      * device and inode of the current drive then we found a solution.
300      * If there is another drive pointing to a deeper position in
301      * the file tree, we want to find that one, not the earlier solution.
302      */
303     int drive, rootdrive = -1;
304     char buffer[MAX_PATHNAME_LEN];
305     char *next = buffer;
306     const char *p = *path;
307     struct stat st;
308
309     strcpy( buffer, "/" );
310     for (;;)
311     {
312         if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
313
314         /* Find the drive */
315
316         for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
317         {
318            if (!DOSDrives[drive].root ||
319                (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
320
321            if ((DOSDrives[drive].dev == st.st_dev) &&
322                (DOSDrives[drive].ino == st.st_ino))
323            {
324                rootdrive = drive;
325                *path = p;
326            }
327         }
328
329         /* Get the next path component */
330
331         *next++ = '/';
332         while ((*p == '/') || (*p == '\\')) p++;
333         if (!*p) break;
334         while (!IS_END_OF_NAME(*p)) *next++ = *p++;
335         *next = 0;
336     }
337     *next = 0;
338
339     if (rootdrive != -1)
340         TRACE("%s -> drive %c:, root='%s', name='%s'\n",
341               buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, *path );
342     return rootdrive;
343 }
344
345
346 /***********************************************************************
347  *           DRIVE_GetRoot
348  */
349 const char * DRIVE_GetRoot( int drive )
350 {
351     if (!DRIVE_IsValid( drive )) return NULL;
352     return DOSDrives[drive].root;
353 }
354
355
356 /***********************************************************************
357  *           DRIVE_GetDosCwd
358  */
359 const char * DRIVE_GetDosCwd( int drive )
360 {
361     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
362     if (!DRIVE_IsValid( drive )) return NULL;
363
364     /* Check if we need to change the directory to the new task. */
365     if (pTask && (pTask->curdrive & 0x80) &&    /* The task drive is valid */
366         ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
367         (DRIVE_LastTask != GetCurrentTask()))   /* and the task changed */
368     {
369         /* Perform the task-switch */
370         if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
371         DRIVE_LastTask = GetCurrentTask();
372     }
373     return DOSDrives[drive].dos_cwd;
374 }
375
376
377 /***********************************************************************
378  *           DRIVE_GetUnixCwd
379  */
380 const char * DRIVE_GetUnixCwd( int drive )
381 {
382     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
383     if (!DRIVE_IsValid( drive )) return NULL;
384
385     /* Check if we need to change the directory to the new task. */
386     if (pTask && (pTask->curdrive & 0x80) &&    /* The task drive is valid */
387         ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
388         (DRIVE_LastTask != GetCurrentTask()))   /* and the task changed */
389     {
390         /* Perform the task-switch */
391         if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
392         DRIVE_LastTask = GetCurrentTask();
393     }
394     return DOSDrives[drive].unix_cwd;
395 }
396
397
398 /***********************************************************************
399  *           DRIVE_GetLabel
400  */
401 const char * DRIVE_GetLabel( int drive )
402 {
403     if (!DRIVE_IsValid( drive )) return NULL;
404     return DOSDrives[drive].label;
405 }
406
407
408 /***********************************************************************
409  *           DRIVE_GetSerialNumber
410  */
411 DWORD DRIVE_GetSerialNumber( int drive )
412 {
413     if (!DRIVE_IsValid( drive )) return 0;
414     return DOSDrives[drive].serial;
415 }
416
417
418 /***********************************************************************
419  *           DRIVE_SetSerialNumber
420  */
421 int DRIVE_SetSerialNumber( int drive, DWORD serial )
422 {
423     if (!DRIVE_IsValid( drive )) return 0;
424     DOSDrives[drive].serial = serial;
425     return 1;
426 }
427
428
429 /***********************************************************************
430  *           DRIVE_GetType
431  */
432 DRIVETYPE DRIVE_GetType( int drive )
433 {
434     if (!DRIVE_IsValid( drive )) return TYPE_INVALID;
435     return DOSDrives[drive].type;
436 }
437
438
439 /***********************************************************************
440  *           DRIVE_GetFlags
441  */
442 UINT DRIVE_GetFlags( int drive )
443 {
444     if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
445     return DOSDrives[drive].flags;
446 }
447
448
449 /***********************************************************************
450  *           DRIVE_Chdir
451  */
452 int DRIVE_Chdir( int drive, const char *path )
453 {
454     DOS_FULL_NAME full_name;
455     char buffer[MAX_PATHNAME_LEN];
456     LPSTR unix_cwd;
457     BY_HANDLE_FILE_INFORMATION info;
458     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
459
460     strcpy( buffer, "A:" );
461     buffer[0] += drive;
462     TRACE("(%c:,%s)\n", buffer[0], path );
463     lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
464
465     if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
466     if (!FILE_Stat( full_name.long_name, &info )) return 0;
467     if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
468     {
469         SetLastError( ERROR_FILE_NOT_FOUND );
470         return 0;
471     }
472     unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
473     while (*unix_cwd == '/') unix_cwd++;
474
475     TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
476                    'A' + drive, unix_cwd, full_name.short_name + 3 );
477
478     HeapFree( SystemHeap, 0, DOSDrives[drive].dos_cwd );
479     HeapFree( SystemHeap, 0, DOSDrives[drive].unix_cwd );
480     DOSDrives[drive].dos_cwd  = HEAP_strdupA( SystemHeap, 0,
481                                               full_name.short_name + 3 );
482     DOSDrives[drive].unix_cwd = HEAP_strdupA( SystemHeap, 0, unix_cwd );
483
484     if (pTask && (pTask->curdrive & 0x80) && 
485         ((pTask->curdrive & ~0x80) == drive))
486     {
487         lstrcpynA( pTask->curdir, full_name.short_name + 2,
488                      sizeof(pTask->curdir) );
489         DRIVE_LastTask = GetCurrentTask();
490     }
491     return 1;
492 }
493
494
495 /***********************************************************************
496  *           DRIVE_Disable
497  */
498 int DRIVE_Disable( int drive  )
499 {
500     if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
501     {
502         SetLastError( ERROR_INVALID_DRIVE );
503         return 0;
504     }
505     DOSDrives[drive].flags |= DRIVE_DISABLED;
506     return 1;
507 }
508
509
510 /***********************************************************************
511  *           DRIVE_Enable
512  */
513 int DRIVE_Enable( int drive  )
514 {
515     if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
516     {
517         SetLastError( ERROR_INVALID_DRIVE );
518         return 0;
519     }
520     DOSDrives[drive].flags &= ~DRIVE_DISABLED;
521     return 1;
522 }
523
524
525 /***********************************************************************
526  *           DRIVE_SetLogicalMapping
527  */
528 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
529 {
530  /* If new_drive is already valid, do nothing and return 0
531     otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
532   
533     DOSDRIVE *old, *new;
534     
535     old = DOSDrives + existing_drive;
536     new = DOSDrives + new_drive;
537
538     if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
539         !old->root ||
540         (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
541     {
542         SetLastError( ERROR_INVALID_DRIVE );
543         return 0;
544     }
545
546     if ( new->root )
547     {
548         TRACE("Can\'t map drive %c to drive %c - drive %c already exists\n",
549               'A' + existing_drive, 'A' + new_drive, 'A' + new_drive );
550         /* it is already mapped there, so return success */
551         if (!strcmp(old->root,new->root))
552             return 1;
553         return 0;
554     }
555
556     new->root = HEAP_strdupA( SystemHeap, 0, old->root );
557     new->dos_cwd = HEAP_strdupA( SystemHeap, 0, old->dos_cwd );
558     new->unix_cwd = HEAP_strdupA( SystemHeap, 0, old->unix_cwd );
559     memcpy ( new->label, old->label, 12 );
560     new->serial = old->serial;
561     new->type = old->type;
562     new->flags = old->flags;
563     new->dev = old->dev;
564     new->ino = old->ino;
565
566     TRACE("Drive %c is now equal to drive %c\n",
567           'A' + new_drive, 'A' + existing_drive );
568
569     return 1;
570 }
571
572
573 /***********************************************************************
574  *           DRIVE_OpenDevice
575  *
576  * Open the drive raw device and return a Unix fd (or -1 on error).
577  */
578 int DRIVE_OpenDevice( int drive, int flags )
579 {
580     if (!DRIVE_IsValid( drive )) return -1;
581     return open( DOSDrives[drive].device, flags );
582 }
583
584
585 /***********************************************************************
586  *           DRIVE_RawRead
587  *
588  * Read raw sectors from a device
589  */
590 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
591 {
592     int fd;
593
594     if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
595     {
596         lseek( fd, begin * 512, SEEK_SET );
597         /* FIXME: check errors */
598         read( fd, dataptr, nr_sect * 512 );
599         close( fd );
600     }
601     else
602     {
603         memset(dataptr, 0, nr_sect * 512);
604                 if (fake_success)
605         {
606                         if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
607                         if (begin == 1) *dataptr = 0xf8;
608                 }
609                 else
610                 return 0;
611     }
612         return 1;
613 }
614
615
616 /***********************************************************************
617  *           DRIVE_RawWrite
618  *
619  * Write raw sectors to a device
620  */
621 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
622 {
623         int fd;
624
625     if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
626     {
627         lseek( fd, begin * 512, SEEK_SET );
628         /* FIXME: check errors */
629         write( fd, dataptr, nr_sect * 512 );
630         close( fd );
631     }
632     else
633         if (!(fake_success))
634                 return 0;
635
636         return 1;
637 }
638
639
640 /***********************************************************************
641  *           DRIVE_GetFreeSpace
642  */
643 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size, 
644                                PULARGE_INTEGER available )
645 {
646     struct statfs info;
647     unsigned long long  bigsize,bigavail=0;
648
649     if (!DRIVE_IsValid(drive))
650     {
651         SetLastError( ERROR_INVALID_DRIVE );
652         return 0;
653     }
654
655 /* FIXME: add autoconf check for this */
656 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
657     if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
658 #else
659     if (statfs( DOSDrives[drive].root, &info) < 0)
660 #endif
661     {
662         FILE_SetDosError();
663         WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
664         return 0;
665     }
666
667     bigsize = (unsigned long long)info.f_bsize 
668       * (unsigned long long)info.f_blocks;
669 #ifdef STATFS_HAS_BAVAIL
670     bigavail = (unsigned long long)info.f_bavail 
671       * (unsigned long long)info.f_bsize;
672 #else
673 # ifdef STATFS_HAS_BFREE
674     bigavail = (unsigned long long)info.f_bfree 
675       * (unsigned long long)info.f_bsize;
676 # else
677 #  error "statfs has no bfree/bavail member!"
678 # endif
679 #endif
680     size->s.LowPart = (DWORD)bigsize;
681     size->s.HighPart = (DWORD)(bigsize>>32);
682     available->s.LowPart = (DWORD)bigavail;
683     available->s.HighPart = (DWORD)(bigavail>>32);
684     return 1;
685 }
686
687 /***********************************************************************
688  *       DRIVE_GetCurrentDirectory
689  * Returns "X:\\path\\etc\\".
690  *
691  * Despite the API description, return required length including the 
692  * terminating null when buffer too small. This is the real behaviour.
693 */
694
695 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
696 {
697     UINT ret;
698     const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
699
700     assert(s);
701     ret = strlen(s) + 3; /* length of WHOLE current directory */
702     if (ret >= buflen) return ret + 1;
703     lstrcpynA( buf, "A:\\", MIN( 4, buflen ) );
704     if (buflen) buf[0] += DRIVE_GetCurrentDrive();
705     if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
706     return ret;
707 }
708
709 /***********************************************************************
710  *           GetDiskFreeSpace16   (KERNEL.422)
711  */
712 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
713                                   LPDWORD sector_bytes, LPDWORD free_clusters,
714                                   LPDWORD total_clusters )
715 {
716     return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
717                                 free_clusters, total_clusters );
718 }
719
720
721 /***********************************************************************
722  *           GetDiskFreeSpace32A   (KERNEL32.206)
723  *
724  * Fails if expression resulting from current drive's dir and "root"
725  * is not a root dir of the target drive.
726  *
727  * UNDOC: setting some LPDWORDs to NULL is perfectly possible 
728  * if the corresponding info is unneeded.
729  *
730  * FIXME: needs to support UNC names from Win95 OSR2 on.
731  *
732  * Behaviour under Win95a:
733  * CurrDir     root   result
734  * "E:\\TEST"  "E:"   FALSE
735  * "E:\\"      "E:"   TRUE
736  * "E:\\"      "E"    FALSE
737  * "E:\\"      "\\"   TRUE
738  * "E:\\TEST"  "\\"   TRUE
739  * "E:\\TEST"  ":\\"  FALSE
740  * "E:\\TEST"  "E:\\" TRUE
741  * "E:\\TEST"  ""     FALSE
742  * "E:\\"      ""     FALSE (!)
743  * "E:\\"      0x0    TRUE
744  * "E:\\TEST"  0x0    TRUE  (!)
745  * "E:\\TEST"  "C:"   TRUE  (when CurrDir of "C:" set to "\\")
746  * "E:\\TEST"  "C:"   FALSE (when CurrDir of "C:" set to "\\TEST")
747  */
748 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
749                                    LPDWORD sector_bytes, LPDWORD free_clusters,
750                                    LPDWORD total_clusters )
751 {
752     int drive;
753     ULARGE_INTEGER size,available;
754     LPCSTR path;
755     DWORD cluster_sec;
756
757     if ((!root) || (strcmp(root,"\\") == 0))
758         drive = DRIVE_GetCurrentDrive();
759     else
760     if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
761     {
762         drive = toupper(root[0]) - 'A';
763         path = &root[2];
764         if (path[0] == '\0')
765             path = DRIVE_GetDosCwd(drive);
766         else
767         if (path[0] == '\\')
768             path++;
769         if (strlen(path)) /* oops, we are in a subdir */
770             return FALSE;
771     }
772     else
773         return FALSE;
774
775     if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
776
777     /* Cap the size and available at 2GB as per specs.  */
778     if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
779         {
780           size.s.HighPart = 0;
781           size.s.LowPart = 0x7fffffff;
782         }
783     if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
784       {
785         available.s.HighPart =0;
786         available.s.LowPart = 0x7fffffff;
787       }
788     if (DRIVE_GetType(drive)==TYPE_CDROM) {
789         if (sector_bytes)
790         *sector_bytes    = 2048;
791         size.s.LowPart            /= 2048;
792         available.s.LowPart       /= 2048;
793     } else {
794         if (sector_bytes)
795         *sector_bytes    = 512;
796         size.s.LowPart            /= 512;
797         available.s.LowPart       /= 512;
798     }
799     /* fixme: probably have to adjust those variables too for CDFS */
800     cluster_sec = 1;
801     while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
802
803     if (cluster_sectors)
804         *cluster_sectors = cluster_sec;
805     if (free_clusters)
806         *free_clusters   = available.s.LowPart / cluster_sec;
807     if (total_clusters)
808         *total_clusters  = size.s.LowPart / cluster_sec;
809     return TRUE;
810 }
811
812
813 /***********************************************************************
814  *           GetDiskFreeSpace32W   (KERNEL32.207)
815  */
816 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
817                                    LPDWORD sector_bytes, LPDWORD free_clusters,
818                                    LPDWORD total_clusters )
819 {
820     LPSTR xroot;
821     BOOL ret;
822
823     xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
824     ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
825                                free_clusters, total_clusters );
826     HeapFree( GetProcessHeap(), 0, xroot );
827     return ret;
828 }
829
830
831 /***********************************************************************
832  *           GetDiskFreeSpaceEx32A   (KERNEL32.871)
833  *
834  *  This function is used to aquire the size of the available and
835  *  total space on a logical volume.
836  *
837  * RETURNS
838  *
839  *  Zero on failure, nonzero upon success. Use GetLastError to obtain
840  *  detailed error information.
841  *
842  */
843 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
844                                      PULARGE_INTEGER avail,
845                                      PULARGE_INTEGER total,
846                                      PULARGE_INTEGER totalfree)
847 {
848     int drive;
849     ULARGE_INTEGER size,available;
850
851     if (!root) drive = DRIVE_GetCurrentDrive();
852     else
853     {
854         if ((root[1]) && ((root[1] != ':') || (root[2] != '\\')))
855         {
856             FIXME("there are valid root names which are not supported yet\n");
857             /* ..like UNC names, for instance. */
858
859             WARN("invalid root '%s'\n", root );
860             return FALSE;
861         }
862         drive = toupper(root[0]) - 'A';
863     }
864
865     if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
866
867     if (total)
868     {
869         total->s.HighPart = size.s.HighPart;
870         total->s.LowPart = size.s.LowPart ;
871     }
872
873     if (totalfree)
874     {
875         totalfree->s.HighPart = available.s.HighPart;
876         totalfree->s.LowPart = available.s.LowPart ;
877     }
878
879     if (avail)
880     {
881         if (FIXME_ON(dosfs))
882         {
883             /* On Windows2000, we need to check the disk quota
884                allocated for the user owning the calling process. We
885                don't want to be more obtrusive than necessary with the
886                FIXME messages, so don't print the FIXME unless Wine is
887                actually masquerading as Windows2000. */
888
889             OSVERSIONINFOA ovi;
890             ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
891             if (GetVersionExA(&ovi))
892             {
893               if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
894                   FIXME("no per-user quota support yet\n");
895             }
896         }
897
898         /* Quick hack, should eventually be fixed to work 100% with
899            Windows2000 (see comment above). */
900         avail->s.HighPart = available.s.HighPart;
901         avail->s.LowPart = available.s.LowPart ;
902     }
903
904     return TRUE;
905 }
906
907 /***********************************************************************
908  *           GetDiskFreeSpaceEx32W   (KERNEL32.873)
909  */
910 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
911                                      PULARGE_INTEGER total,
912                                      PULARGE_INTEGER  totalfree)
913 {
914     LPSTR xroot;
915     BOOL ret;
916
917     xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
918     ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
919     HeapFree( GetProcessHeap(), 0, xroot );
920     return ret;
921 }
922
923 /***********************************************************************
924  *           GetDriveType16   (KERNEL.136)
925  * This functions returns the drivetype of a drive in Win16. 
926  * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
927  * remote drive API. The returnvalue DRIVE_REMOTE for CD-ROMs has been
928  * verified on Win3.11 and Windows 95. Some programs rely on it, so don't
929  * do any pseudo-clever changes.
930  *
931  * RETURNS
932  *      drivetype DRIVE_xxx
933  */
934 UINT16 WINAPI GetDriveType16(
935         UINT16 drive    /* [in] number (NOT letter) of drive */
936 ) {
937     TRACE("(%c:)\n", 'A' + drive );
938     switch(DRIVE_GetType(drive))
939     {
940     case TYPE_FLOPPY:  return DRIVE_REMOVABLE;
941     case TYPE_HD:      return DRIVE_FIXED;
942     case TYPE_CDROM:   return DRIVE_REMOTE;
943     case TYPE_NETWORK: return DRIVE_REMOTE;
944     case TYPE_INVALID:
945     default:           return DRIVE_CANNOTDETERMINE;
946     }
947 }
948
949
950 /***********************************************************************
951  *           GetDriveType32A   (KERNEL32.208)
952  *
953  * Returns the type of the disk drive specified.  If root is NULL the
954  * root of the current directory is used.
955  *
956  * RETURNS
957  *
958  *  Type of drive (from Win32 SDK):
959  *
960  *   DRIVE_UNKNOWN     unable to find out anything about the drive
961  *   DRIVE_NO_ROOT_DIR nonexistand root dir
962  *   DRIVE_REMOVABLE   the disk can be removed from the machine
963  *   DRIVE_FIXED       the disk can not be removed from the machine
964  *   DRIVE_REMOTE      network disk
965  *   DRIVE_CDROM       CDROM drive
966  *   DRIVE_RAMDISK     virtual disk in ram
967  *
968  *   DRIVE_DOESNOTEXIST    XXX Not valid return value
969  *   DRIVE_CANNOTDETERMINE XXX Not valid return value
970  *   
971  * BUGS
972  *
973  *  Currently returns DRIVE_DOESNOTEXIST and DRIVE_CANNOTDETERMINE
974  *  when it really should return DRIVE_NO_ROOT_DIR and DRIVE_UNKNOWN.
975  *  Why where the former defines used?
976  *
977  *  DRIVE_RAMDISK is unsupported.
978  */
979 UINT WINAPI GetDriveTypeA(LPCSTR root /* String describing drive */)
980 {
981     int drive;
982     TRACE("(%s)\n", debugstr_a(root));
983
984     if (NULL == root) drive = DRIVE_GetCurrentDrive();
985     else
986     {
987         if ((root[1]) && (root[1] != ':'))
988         {
989             WARN("invalid root '%s'\n", debugstr_a(root));
990             return DRIVE_DOESNOTEXIST;
991         }
992         drive = toupper(root[0]) - 'A';
993     }
994     switch(DRIVE_GetType(drive))
995     {
996     case TYPE_FLOPPY:  return DRIVE_REMOVABLE;
997     case TYPE_HD:      return DRIVE_FIXED;
998     case TYPE_CDROM:   return DRIVE_CDROM;
999     case TYPE_NETWORK: return DRIVE_REMOTE;
1000     case TYPE_INVALID: return DRIVE_DOESNOTEXIST;
1001     default:           return DRIVE_CANNOTDETERMINE;
1002     }
1003 }
1004
1005
1006 /***********************************************************************
1007  *           GetDriveType32W   (KERNEL32.209)
1008  */
1009 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1010 {
1011     LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1012     UINT ret = GetDriveTypeA( xpath );
1013     HeapFree( GetProcessHeap(), 0, xpath );
1014     return ret;
1015 }
1016
1017
1018 /***********************************************************************
1019  *           GetCurrentDirectory16   (KERNEL.411)
1020  */
1021 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1022 {
1023     return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1024 }
1025
1026
1027 /***********************************************************************
1028  *           GetCurrentDirectory32A   (KERNEL32.196)
1029  */
1030 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1031 {
1032     UINT ret;
1033     char longname[MAX_PATHNAME_LEN];
1034     char shortname[MAX_PATHNAME_LEN];
1035     ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1036     if ( ret > MAX_PATHNAME_LEN ) {
1037       ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1038       return ret;
1039     }
1040     GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1041     ret = lstrlenA( longname ) + 1;
1042     if (ret > buflen) return ret;
1043     lstrcpyA(buf, longname);
1044     return ret - 1;
1045 }
1046
1047 /***********************************************************************
1048  *           GetCurrentDirectory32W   (KERNEL32.197)
1049  */
1050 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1051 {
1052     LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1053     UINT ret = GetCurrentDirectoryA( buflen, xpath );
1054     if (ret < buflen) lstrcpyAtoW ( buf, xpath );
1055     HeapFree( GetProcessHeap(), 0, xpath );
1056     return ret;
1057 }
1058
1059
1060 /***********************************************************************
1061  *           SetCurrentDirectory   (KERNEL.412)
1062  */
1063 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1064 {
1065     return SetCurrentDirectoryA( dir );
1066 }
1067
1068
1069 /***********************************************************************
1070  *           SetCurrentDirectory32A   (KERNEL32.479)
1071  */
1072 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1073 {
1074     int olddrive, drive = DRIVE_GetCurrentDrive();
1075
1076     if (!dir) {
1077         ERR_(file)("(NULL)!\n");
1078         return FALSE;
1079     }
1080     if (dir[0] && (dir[1]==':'))
1081     {
1082         drive = tolower( *dir ) - 'a';
1083         dir += 2;
1084     }
1085
1086     /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1087        sets pTask->curdir only if pTask->curdrive is drive */
1088     olddrive = drive; /* in case DRIVE_Chdir fails */
1089     if (!(DRIVE_SetCurrentDrive( drive )))
1090         return FALSE;
1091     /* FIXME: what about empty strings? Add a \\ ? */
1092     if (!DRIVE_Chdir( drive, dir )) {
1093         DRIVE_SetCurrentDrive(olddrive);
1094         return FALSE;
1095     }
1096     return TRUE;
1097 }
1098
1099
1100 /***********************************************************************
1101  *           SetCurrentDirectory32W   (KERNEL32.480)
1102  */
1103 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1104 {
1105     LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1106     BOOL res = SetCurrentDirectoryA( dir );
1107     HeapFree( GetProcessHeap(), 0, dir );
1108     return res;
1109 }
1110
1111
1112 /***********************************************************************
1113  *           GetLogicalDriveStrings32A   (KERNEL32.231)
1114  */
1115 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1116 {
1117     int drive, count;
1118
1119     for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1120         if (DRIVE_IsValid(drive)) count++;
1121     if ((count * 4) + 1 <= len)
1122     {
1123         LPSTR p = buffer;
1124         for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1125             if (DRIVE_IsValid(drive))
1126             {
1127                 *p++ = 'a' + drive;
1128                 *p++ = ':';
1129                 *p++ = '\\';
1130                 *p++ = '\0';
1131             }
1132         *p = '\0';
1133        return count * 4;
1134     }
1135     else
1136       return (count * 4) + 1;/* account for terminating null */
1137     /* The API tells about these different return values */
1138 }
1139
1140
1141 /***********************************************************************
1142  *           GetLogicalDriveStrings32W   (KERNEL32.232)
1143  */
1144 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1145 {
1146     int drive, count;
1147
1148     for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1149         if (DRIVE_IsValid(drive)) count++;
1150     if (count * 4 * sizeof(WCHAR) <= len)
1151     {
1152         LPWSTR p = buffer;
1153         for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1154             if (DRIVE_IsValid(drive))
1155             {
1156                 *p++ = (WCHAR)('a' + drive);
1157                 *p++ = (WCHAR)':';
1158                 *p++ = (WCHAR)'\\';
1159                 *p++ = (WCHAR)'\0';
1160             }
1161         *p = (WCHAR)'\0';
1162     }
1163     return count * 4 * sizeof(WCHAR);
1164 }
1165
1166
1167 /***********************************************************************
1168  *           GetLogicalDrives   (KERNEL32.233)
1169  */
1170 DWORD WINAPI GetLogicalDrives(void)
1171 {
1172     DWORD ret = 0;
1173     int drive;
1174
1175     for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1176         if (DRIVE_IsValid(drive)) ret |= (1 << drive);
1177     return ret;
1178 }
1179
1180
1181 /***********************************************************************
1182  *           GetVolumeInformation32A   (KERNEL32.309)
1183  */
1184 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1185                                        DWORD label_len, DWORD *serial,
1186                                        DWORD *filename_len, DWORD *flags,
1187                                        LPSTR fsname, DWORD fsname_len )
1188 {
1189     int drive;
1190     char *cp;
1191
1192     /* FIXME, SetLastErrors missing */
1193
1194     if (!root) drive = DRIVE_GetCurrentDrive();
1195     else
1196     {
1197         if ((root[1]) && (root[1] != ':'))
1198         {
1199             WARN("invalid root '%s'\n",root);
1200             return FALSE;
1201         }
1202         drive = toupper(root[0]) - 'A';
1203     }
1204     if (!DRIVE_IsValid( drive )) return FALSE;
1205     if (label)
1206     {
1207        lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1208        for (cp = label; *cp; cp++);
1209        while (cp != label && *(cp-1) == ' ') cp--;
1210        *cp = '\0';
1211     }
1212     if (serial) *serial = DRIVE_GetSerialNumber(drive);
1213
1214     /* Set the filesystem information */
1215     /* Note: we only emulate a FAT fs at the present */
1216
1217     if (filename_len) {
1218         if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1219             *filename_len = 12;
1220         else
1221             *filename_len = 255;
1222     }
1223     if (flags)
1224       {
1225        *flags=0;
1226        if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1227          *flags|=FS_CASE_SENSITIVE;
1228        if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1229          *flags|=FS_CASE_IS_PRESERVED ;
1230       }
1231     if (fsname) {
1232         /* Diablo checks that return code ... */
1233         if (DRIVE_GetType(drive)==TYPE_CDROM)
1234             lstrcpynA( fsname, "CDFS", fsname_len );
1235         else
1236             lstrcpynA( fsname, "FAT", fsname_len );
1237     }
1238     return TRUE;
1239 }
1240
1241
1242 /***********************************************************************
1243  *           GetVolumeInformation32W   (KERNEL32.310)
1244  */
1245 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1246                                        DWORD label_len, DWORD *serial,
1247                                        DWORD *filename_len, DWORD *flags,
1248                                        LPWSTR fsname, DWORD fsname_len )
1249 {
1250     LPSTR xroot    = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1251     LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1252     LPSTR xfsname  = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1253     BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1254                                           filename_len, flags, xfsname,
1255                                           fsname_len );
1256     if (ret)
1257     {
1258         if (label) lstrcpyAtoW( label, xvolname );
1259         if (fsname) lstrcpyAtoW( fsname, xfsname );
1260     }
1261     HeapFree( GetProcessHeap(), 0, xroot );
1262     HeapFree( GetProcessHeap(), 0, xvolname );
1263     HeapFree( GetProcessHeap(), 0, xfsname );
1264     return ret;
1265 }
1266
1267 /***********************************************************************
1268  *           SetVolumeLabelA   (KERNEL32.675)
1269  */
1270 BOOL WINAPI SetVolumeLabelA(LPCSTR rootpath,LPCSTR volname)
1271 {
1272         FIXME("(%s,%s),stub!\n",rootpath,volname);
1273         return TRUE;
1274 }
1275
1276 /***********************************************************************
1277  *           SetVolumeLabelW   (KERNEL32.676)
1278  */
1279 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1280 {
1281     LPSTR xroot, xvol;
1282     BOOL ret;
1283
1284     xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1285     xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1286     ret = SetVolumeLabelA( xroot, xvol );
1287     HeapFree( GetProcessHeap(), 0, xroot );
1288     HeapFree( GetProcessHeap(), 0, xvol );
1289     return ret;
1290 }