Release 960705
[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 <ctype.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13
14 #if defined(__linux__) || defined(sun)
15 #include <sys/vfs.h>
16 #endif
17 #if defined(__NetBSD__) || defined(__FreeBSD__)
18 #include <sys/param.h>
19 #include <sys/mount.h>
20 #include <sys/errno.h>
21 #endif
22 #if defined(__svr4__) || defined(_SCO_DS)
23 #include <sys/statfs.h>
24 #endif
25
26 #include "windows.h"
27 #include "winbase.h"
28 #include "dos_fs.h"
29 #include "drive.h"
30 #include "file.h"
31 #include "msdos.h"
32 #include "options.h"
33 #include "task.h"
34 #include "xmalloc.h"
35 #include "string32.h"
36 #include "stddebug.h"
37 #include "debug.h"
38
39 typedef struct
40 {
41     char     *root;      /* root dir in Unix format without trailing / */
42     char     *dos_cwd;   /* cwd in DOS format without leading or trailing \ */
43     char     *unix_cwd;  /* cwd in Unix format without leading or trailing / */
44     char      label[12]; /* drive label */
45     DWORD     serial;    /* drive serial number */
46     DRIVETYPE type;      /* drive type */
47     BYTE  disabled;      /* disabled flag */
48 } DOSDRIVE;
49
50
51 static const char *DRIVE_Types[] =
52 {
53     "floppy",   /* TYPE_FLOPPY */
54     "hd",       /* TYPE_HD */
55     "cdrom",    /* TYPE_CDROM */
56     "network"   /* TYPE_NETWORK */
57 };
58
59
60 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
61 static int DRIVE_CurDrive = -1;
62
63 static HTASK DRIVE_LastTask = 0;
64
65
66 /***********************************************************************
67  *           DRIVE_GetDriveType
68  */
69 static DRIVETYPE DRIVE_GetDriveType( const char *name )
70 {
71     char buffer[20];
72     int i;
73
74     PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
75     for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
76     {
77         if (!lstrcmpi32A( buffer, DRIVE_Types[i] )) return (DRIVETYPE)i;
78     }
79     fprintf( stderr, "%s: unknown type '%s', defaulting to 'hd'.\n",
80              name, buffer );
81     return TYPE_HD;
82 }
83
84
85 /***********************************************************************
86  *           DRIVE_Init
87  */
88 int DRIVE_Init(void)
89 {
90     int i, len, count = 0;
91     char name[] = "Drive A";
92     char path[MAX_PATHNAME_LEN];
93     char buffer[20];
94     char *p;
95     DOSDRIVE *drive;
96
97     for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
98     {
99         PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
100         if (path[0])
101         {
102             p = path + strlen(path) - 1;
103             while ((p > path) && ((*p == '/') || (*p == '\\'))) *p-- = '\0';
104             drive->root     = xstrdup( path );
105             drive->dos_cwd  = xstrdup( "" );
106             drive->unix_cwd = xstrdup( "" );
107             drive->type     = DRIVE_GetDriveType( name );
108             drive->disabled = 0;
109
110             /* Get the drive label */
111             PROFILE_GetWineIniString( name, "Label", name, drive->label, 12 );
112             if ((len = strlen(drive->label)) < 11)
113             {
114                 /* Pad label with spaces */
115                 memset( drive->label + len, ' ', 11 - len );
116                 drive->label[12] = '\0';
117             }
118
119             /* Get the serial number */
120             PROFILE_GetWineIniString( name, "Serial", "12345678",
121                                       buffer, sizeof(buffer) );
122             drive->serial = strtoul( buffer, NULL, 16 );
123
124             /* Make the first hard disk the current drive */
125             if ((DRIVE_CurDrive == -1) && (drive->type == TYPE_HD))
126                 DRIVE_CurDrive = i;
127
128             count++;
129             dprintf_dosfs( stddeb, "%s: path=%s type=%s label='%s' serial=%08lx\n",
130                            name, path, DRIVE_Types[drive->type],
131                            drive->label, drive->serial );
132         }
133         else dprintf_dosfs( stddeb, "%s: not defined\n", name );
134     }
135
136     if (!count) 
137     {
138         fprintf( stderr, "Warning: no valid DOS drive found, check your configuration file.\n" );
139         /* Create a C drive pointing to Unix root dir */
140         DOSDrives[2].root     = xstrdup( "/" );
141         DOSDrives[2].dos_cwd  = xstrdup( "" );
142         DOSDrives[2].unix_cwd = xstrdup( "" );
143         strcpy( DOSDrives[2].label, "Drive C    " );
144         DOSDrives[2].serial   = 0x12345678;
145         DOSDrives[2].type     = TYPE_HD;
146         DOSDrives[2].disabled = 0;
147         DRIVE_CurDrive = 2;
148     }
149
150     /* Make sure the current drive is valid */
151     if (DRIVE_CurDrive == -1)
152     {
153         for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
154         {
155             if (drive->root && !drive->disabled)
156             {
157                 DRIVE_CurDrive = i;
158                 break;
159             }
160         }
161     }
162
163     return 1;
164 }
165
166
167 /***********************************************************************
168  *           DRIVE_IsValid
169  */
170 int DRIVE_IsValid( int drive )
171 {
172     if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
173     return (DOSDrives[drive].root && !DOSDrives[drive].disabled);
174 }
175
176
177 /***********************************************************************
178  *           DRIVE_GetCurrentDrive
179  */
180 int DRIVE_GetCurrentDrive(void)
181 {
182     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
183     if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
184     return DRIVE_CurDrive;
185 }
186
187
188 /***********************************************************************
189  *           DRIVE_SetCurrentDrive
190  */
191 int DRIVE_SetCurrentDrive( int drive )
192 {
193     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
194     if (!DRIVE_IsValid( drive ))
195     {
196         DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
197         return 0;
198     }
199     dprintf_dosfs( stddeb, "DRIVE_SetCurrentDrive: %c:\n", 'A' + drive );
200     DRIVE_CurDrive = drive;
201     if (pTask) pTask->curdrive = drive | 0x80;
202     return 1;
203 }
204
205
206 /***********************************************************************
207  *           DRIVE_FindDriveRoot
208  *
209  * Find a drive for which the root matches the begginning of the given path.
210  * This can be used to translate a Unix path into a drive + DOS path.
211  * Return value is the drive, or -1 on error. On success, path is modified
212  * to point to the beginning of the DOS path.
213  * FIXME: this only does a textual comparison of the path names, and won't
214  *        work well in the presence of symbolic links.
215  */
216 int DRIVE_FindDriveRoot( const char **path )
217 {
218     int drive, rootdrive = -1;
219     const char *p1, *p2;
220
221     dprintf_dosfs( stddeb, "DRIVE_FindDriveRoot: searching '%s'\n", *path );
222     for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
223     {
224         if (!DOSDrives[drive].root || DOSDrives[drive].disabled) continue;
225         p1 = *path;
226         p2 = DOSDrives[drive].root;
227         dprintf_dosfs( stddeb, "DRIVE_FindDriveRoot: checking %c: '%s'\n",
228                        'A' + drive, p2 );
229         
230         while (*p2 == '/') p2++;
231         if (!*p2)
232         {
233             rootdrive = drive;
234             continue;  /* Look if there's a better match */
235         }
236         for (;;)
237         {
238             while ((*p1 == '\\') || (*p1 == '/')) p1++;
239             while (*p2 == '/') p2++;
240             while ((*p1 == *p2) && (*p2) && (*p2 != '/')) p1++, p2++;
241             if (!*p2)
242             {
243                 if (IS_END_OF_NAME(*p1)) /* OK, found it */
244                 {
245                     *path = p1;
246                     return drive;
247                 }
248             }
249             else if (*p2 == '/')
250             {
251                 if (IS_END_OF_NAME(*p1))
252                     continue;  /* Go to next path element */
253             }
254             break;  /* No match, go to next drive */
255         }
256     }
257     return rootdrive;
258 }
259
260
261 /***********************************************************************
262  *           DRIVE_GetRoot
263  */
264 const char * DRIVE_GetRoot( int drive )
265 {
266     if (!DRIVE_IsValid( drive )) return NULL;
267     return DOSDrives[drive].root;
268 }
269
270
271 /***********************************************************************
272  *           DRIVE_GetDosCwd
273  */
274 const char * DRIVE_GetDosCwd( int drive )
275 {
276     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
277     if (!DRIVE_IsValid( drive )) return NULL;
278
279     /* Check if we need to change the directory to the new task. */
280     if (pTask && (pTask->curdrive & 0x80) &&    /* The task drive is valid */
281         ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
282         (DRIVE_LastTask != GetCurrentTask()))   /* and the task changed */
283     {
284         /* Perform the task-switch */
285         if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
286         DRIVE_LastTask = GetCurrentTask();
287     }
288     return DOSDrives[drive].dos_cwd;
289 }
290
291
292 /***********************************************************************
293  *           DRIVE_GetUnixCwd
294  */
295 const char * DRIVE_GetUnixCwd( int drive )
296 {
297     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
298     if (!DRIVE_IsValid( drive )) return NULL;
299
300     /* Check if we need to change the directory to the new task. */
301     if (pTask && (pTask->curdrive & 0x80) &&    /* The task drive is valid */
302         ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
303         (DRIVE_LastTask != GetCurrentTask()))   /* and the task changed */
304     {
305         /* Perform the task-switch */
306         if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
307         DRIVE_LastTask = GetCurrentTask();
308     }
309     return DOSDrives[drive].unix_cwd;
310 }
311
312
313 /***********************************************************************
314  *           DRIVE_GetLabel
315  */
316 const char * DRIVE_GetLabel( int drive )
317 {
318     if (!DRIVE_IsValid( drive )) return NULL;
319     return DOSDrives[drive].label;
320 }
321
322
323 /***********************************************************************
324  *           DRIVE_GetSerialNumber
325  */
326 DWORD DRIVE_GetSerialNumber( int drive )
327 {
328     if (!DRIVE_IsValid( drive )) return 0;
329     return DOSDrives[drive].serial;
330 }
331
332
333 /***********************************************************************
334  *           DRIVE_SetSerialNumber
335  */
336 int DRIVE_SetSerialNumber( int drive, DWORD serial )
337 {
338     if (!DRIVE_IsValid( drive )) return 0;
339     DOSDrives[drive].serial = serial;
340     return 1;
341 }
342
343
344 /***********************************************************************
345  *           DRIVE_GetType
346  */
347 DRIVETYPE DRIVE_GetType( int drive )
348 {
349     if (!DRIVE_IsValid( drive )) return TYPE_INVALID;
350     return DOSDrives[drive].type;
351 }
352
353
354 /***********************************************************************
355  *           DRIVE_Chdir
356  */
357 int DRIVE_Chdir( int drive, const char *path )
358 {
359     char buffer[MAX_PATHNAME_LEN];
360     const char *unix_cwd, *dos_cwd;
361     BYTE attr;
362     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
363
364     dprintf_dosfs( stddeb, "DRIVE_Chdir(%c:,%s)\n", 'A' + drive, path );
365     strcpy( buffer, "A:" );
366     buffer[0] += drive;
367     lstrcpyn32A( buffer + 2, path, sizeof(buffer) - 2 );
368
369     if (!(unix_cwd = DOSFS_GetUnixFileName( buffer, TRUE ))) return 0;
370     if (!FILE_Stat( unix_cwd, &attr, NULL, NULL, NULL )) return 0;
371     if (!(attr & FA_DIRECTORY))
372     {
373         DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
374         return 0;
375     }
376     unix_cwd += strlen( DOSDrives[drive].root );
377     while (*unix_cwd == '/') unix_cwd++;
378     buffer[2] = '/';
379     lstrcpyn32A( buffer + 3, unix_cwd, sizeof(buffer) - 3 );
380     if (!(dos_cwd = DOSFS_GetDosTrueName( buffer, TRUE ))) return 0;
381
382     dprintf_dosfs( stddeb, "DRIVE_Chdir(%c:): unix_cwd=%s dos_cwd=%s\n",
383                    'A' + drive, unix_cwd, dos_cwd + 3 );
384
385     free( DOSDrives[drive].dos_cwd );
386     free( DOSDrives[drive].unix_cwd );
387     DOSDrives[drive].dos_cwd  = xstrdup( dos_cwd + 3 );
388     DOSDrives[drive].unix_cwd = xstrdup( unix_cwd );
389
390     if (pTask && (pTask->curdrive & 0x80) && 
391         ((pTask->curdrive & ~0x80) == drive))
392     {
393         lstrcpyn32A( pTask->curdir, dos_cwd + 2, sizeof(pTask->curdir) );
394         DRIVE_LastTask = GetCurrentTask();
395     }
396     return 1;
397 }
398
399
400 /***********************************************************************
401  *           DRIVE_Disable
402  */
403 int DRIVE_Disable( int drive  )
404 {
405     if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
406     {
407         DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
408         return 0;
409     }
410     DOSDrives[drive].disabled = 1;
411     return 1;
412 }
413
414
415 /***********************************************************************
416  *           DRIVE_Enable
417  */
418 int DRIVE_Enable( int drive  )
419 {
420     if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
421     {
422         DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
423         return 0;
424     }
425     DOSDrives[drive].disabled = 0;
426     return 1;
427 }
428
429
430 /***********************************************************************
431  *           DRIVE_GetFreeSpace
432  */
433 static int DRIVE_GetFreeSpace( int drive, DWORD *size, DWORD *available )
434 {
435     struct statfs info;
436
437     if (!DRIVE_IsValid(drive))
438     {
439         DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
440         return 0;
441     }
442
443 #if defined(__svr4__) || defined(_SCO_DS)
444     if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
445 #else
446     if (statfs( DOSDrives[drive].root, &info) < 0)
447 #endif
448     {
449         FILE_SetDosError();
450         fprintf(stderr,"dosfs: cannot do statfs(%s)\n", DOSDrives[drive].root);
451         return 0;
452     }
453
454     *size = info.f_bsize * info.f_blocks;
455 #if defined(__svr4__) || defined(_SCO_DS)
456     *available = info.f_bfree * info.f_bsize;
457 #else
458     *available = info.f_bavail * info.f_bsize;
459 #endif
460     return 1;
461 }
462
463
464 /***********************************************************************
465  *           GetDiskFreeSpace16   (KERNEL.422)
466  */
467 BOOL16 GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
468                            LPDWORD sector_bytes, LPDWORD free_clusters,
469                            LPDWORD total_clusters )
470 {
471     return GetDiskFreeSpace32A( root, cluster_sectors, sector_bytes,
472                                 free_clusters, total_clusters );
473 }
474
475
476 /***********************************************************************
477  *           GetDiskFreeSpaceA   (KERNEL32.206)
478  */
479 BOOL32 GetDiskFreeSpace32A( LPCSTR root, LPDWORD cluster_sectors,
480                             LPDWORD sector_bytes, LPDWORD free_clusters,
481                             LPDWORD total_clusters )
482 {
483     int drive;
484     DWORD size,available;
485
486     if (!root) drive = DRIVE_GetCurrentDrive();
487     else
488     {
489         if ((root[1] != ':') || (root[2] != '\\'))
490         {
491             fprintf( stderr, "GetDiskFreeSpaceA: invalid root '%s'\n", root );
492             return FALSE;
493         }
494         drive = toupper(root[0]) - 'A';
495     }
496     if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
497
498     *sector_bytes    = 512;
499     size            /= 512;
500     available       /= 512;
501     *cluster_sectors = 1;
502     while (*cluster_sectors * 65530 < size) *cluster_sectors *= 2;
503     *free_clusters   = available/ *cluster_sectors;
504     *total_clusters  = size/ *cluster_sectors;
505     return TRUE;
506 }
507
508
509 /***********************************************************************
510  *           GetDiskFreeSpaceW   (KERNEL32.207)
511  */
512 BOOL32 GetDiskFreeSpace32W( LPCWSTR root, LPDWORD cluster_sectors,
513                             LPDWORD sector_bytes, LPDWORD free_clusters,
514                             LPDWORD total_clusters )
515 {
516     LPSTR xroot;
517     BOOL ret;
518
519     xroot = STRING32_DupUniToAnsi(root);
520     ret = GetDiskFreeSpace32A( xroot,cluster_sectors, sector_bytes,
521                                free_clusters, total_clusters );
522     free( xroot );
523     return ret;
524 }
525
526
527 /***********************************************************************
528  *           GetDriveType16   (KERNEL.136)
529  */
530 UINT16 GetDriveType16( UINT16 drive )
531 {
532     dprintf_dosfs( stddeb, "GetDriveType(%c:)\n", 'A' + drive );
533     switch(DRIVE_GetType(drive))
534     {
535     case TYPE_FLOPPY:  return DRIVE_REMOVABLE;
536     case TYPE_HD:      return DRIVE_FIXED;
537     case TYPE_CDROM:   return DRIVE_REMOVABLE;
538     case TYPE_NETWORK: return DRIVE_REMOTE;
539     case TYPE_INVALID:
540     default:           return DRIVE_CANNOTDETERMINE;
541     }
542 }
543
544
545 /***********************************************************************
546  *           GetDriveType32A   (KERNEL32.208)
547  */
548 UINT32 GetDriveType32A( LPCSTR root )
549 {
550     dprintf_dosfs( stddeb, "GetDriveType32A(%s)\n", root );
551     if ((root[1] != ':') || (root[2] != '\\'))
552     {
553         fprintf( stderr, "GetDriveType32A: invalid root '%s'\n", root );
554         return DRIVE_DOESNOTEXIST;
555     }
556     switch(DRIVE_GetType(toupper(root[0]) - 'A'))
557     {
558     case TYPE_FLOPPY:  return DRIVE_REMOVABLE;
559     case TYPE_HD:      return DRIVE_FIXED;
560     case TYPE_CDROM:   return DRIVE_REMOVABLE;
561     case TYPE_NETWORK: return DRIVE_REMOTE;
562     case TYPE_INVALID:
563     default:           return DRIVE_CANNOTDETERMINE;
564     }
565 }
566
567
568 /***********************************************************************
569  *           GetDriveType32W   (KERNEL32.209)
570  */
571 UINT32 GetDriveType32W( LPCWSTR root )
572 {
573     LPSTR xpath=STRING32_DupUniToAnsi(root);
574     UINT32 ret;
575
576     ret = GetDriveType32A(xpath);
577     free(xpath);
578     return ret;
579 }
580
581
582 /***********************************************************************
583  *           GetCurrentDirectory16   (KERNEL.411)
584  */
585 UINT16 GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
586 {
587     return (UINT16)GetCurrentDirectory32A( buflen, buf );
588 }
589
590
591 /***********************************************************************
592  *           GetCurrentDirectory32A   (KERNEL32.196)
593  *
594  * Returns "X:\\path\\etc\\".
595  */
596 UINT32 GetCurrentDirectory32A( UINT32 buflen, LPSTR buf )
597 {
598     char *pref = "A:\\";
599     const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
600     if (!s)
601     {
602         *buf = '\0';
603         return 0;
604     }
605     lstrcpyn32A( buf, pref, 3 );
606     if (buflen) buf[0] += DRIVE_GetCurrentDrive();
607     if (buflen >= 3) lstrcpyn32A( buf + 3, s, buflen - 3 );
608     return strlen(s) + 3; /* length of WHOLE current directory */
609 }
610
611
612 /***********************************************************************
613  *           GetCurrentDirectory32W   (KERNEL32.197)
614  */
615 UINT32 GetCurrentDirectory32W( UINT32 buflen, LPWSTR buf )
616 {
617     LPSTR xpath=(char*)xmalloc(buflen+1);
618     UINT32 ret;
619
620     ret = GetCurrentDirectory32A(buflen,xpath);
621     STRING32_AnsiToUni(buf,xpath);
622     free(xpath);
623     return ret;
624 }
625
626
627 /***********************************************************************
628  *           SetCurrentDirectory   (KERNEL.412)
629  */
630 BOOL32 SetCurrentDirectory( LPCSTR dir )
631 {
632     return DRIVE_Chdir( DRIVE_GetCurrentDrive(), dir );
633 }
634
635
636 /***********************************************************************
637  *           GetLogicalDriveStrings32A   (KERNEL32.231)
638  */
639 UINT32 GetLogicalDriveStrings32A( UINT32 len, LPSTR buffer )
640 {
641     int drive, count;
642
643     for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
644         if (DRIVE_IsValid(drive)) count++;
645     if (count * 4 * sizeof(char) <= len)
646     {
647         LPSTR p = buffer;
648         for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
649             if (DRIVE_IsValid(drive))
650             {
651                 *p++ = 'a' + drive;
652                 *p++ = ':';
653                 *p++ = '\\';
654                 *p++ = '\0';
655             }
656         *p = '\0';
657     }
658     return count * 4 * sizeof(char);
659 }
660
661
662 /***********************************************************************
663  *           GetLogicalDriveStrings32W   (KERNEL32.232)
664  */
665 UINT32 GetLogicalDriveStrings32W( UINT32 len, LPWSTR buffer )
666 {
667     int drive, count;
668
669     for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
670         if (DRIVE_IsValid(drive)) count++;
671     if (count * 4 * sizeof(WCHAR) <= len)
672     {
673         LPWSTR p = buffer;
674         for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
675             if (DRIVE_IsValid(drive))
676             {
677                 *p++ = (WCHAR)('a' + drive);
678                 *p++ = (WCHAR)':';
679                 *p++ = (WCHAR)'\\';
680                 *p++ = (WCHAR)'\0';
681             }
682         *p = (WCHAR)'\0';
683     }
684     return count * 4 * sizeof(WCHAR);
685 }
686
687
688 /***********************************************************************
689  *           GetLogicalDrives   (KERNEL32.233)
690  */
691 DWORD GetLogicalDrives(void)
692 {
693     DWORD ret = 0;
694     int drive;
695
696     for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
697         if (DRIVE_IsValid(drive)) ret |= (1 << drive);
698     return ret;
699 }
700
701
702 /***********************************************************************
703  *           GetVolumeInformation32A   (KERNEL32.309)
704  */
705 BOOL32 GetVolumeInformation32A( LPCSTR root, LPSTR label, DWORD label_len,
706                                 DWORD *serial, DWORD *filename_len,
707                                 DWORD *flags, LPSTR fsname, DWORD fsname_len )
708 {
709     int drive;
710
711     /* FIXME, SetLastErrors missing */
712
713     if (!root) drive = DRIVE_GetCurrentDrive();
714     else
715     {
716         if ((root[1] != ':') || (root[2] != '\\'))
717         {
718             fprintf( stderr, "GetVolumeInformation: invalid root '%s'\n",root);
719             return FALSE;
720         }
721         drive = toupper(root[0]) - 'A';
722     }
723     if (!DRIVE_IsValid( drive )) return FALSE;
724     if (label) lstrcpyn32A( label, DOSDrives[drive].label, label_len );
725     if (serial) *serial = DOSDrives[drive].serial;
726
727     /* Set the filesystem information */
728     /* Note: we only emulate a FAT fs at the present */
729
730     if (filename_len) *filename_len = 12;
731     if (flags) *flags = 0;
732     if (fsname) lstrcpyn32A( fsname, "FAT", fsname_len );
733     return TRUE;
734 }
735
736
737 /***********************************************************************
738  *           GetVolumeInformation32W   (KERNEL32.310)
739  */
740 BOOL32 GetVolumeInformation32W( LPCWSTR root, LPWSTR label, DWORD label_len,
741                                 DWORD *serial, DWORD *filename_len,
742                                 DWORD *flags, LPWSTR fsname, DWORD fsname_len)
743 {
744     LPSTR xroot    = STRING32_DupUniToAnsi(root);
745     LPSTR xvolname = (char*)xmalloc( label_len );
746     LPSTR xfsname  = (char*)xmalloc( fsname_len );
747     BOOL32 ret = GetVolumeInformation32A( xroot, xvolname, label_len, serial,
748                                           filename_len, flags, xfsname,
749                                           fsname_len );
750     if (ret)
751     {
752         STRING32_AnsiToUni( label, xvolname );
753         STRING32_AnsiToUni( fsname, xfsname );
754     }
755     free(xroot);
756     free(xvolname);
757     free(xfsname);
758     return ret;
759 }