Release 961208
[wine.git] / files / drive.c
blob8a5bf5ad58501c1ee6fa30308ad88bacd270691f
1 /*
2 * DOS drives handling functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
6 */
8 #include <ctype.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
14 #if defined(__linux__) || defined(sun) || defined(hpux)
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
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"
39 typedef struct
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 UINT32 flags; /* drive flags */
48 } DOSDRIVE;
51 static const char * const DRIVE_Types[] =
53 "floppy", /* TYPE_FLOPPY */
54 "hd", /* TYPE_HD */
55 "cdrom", /* TYPE_CDROM */
56 "network" /* TYPE_NETWORK */
60 /* Known filesystem types */
62 typedef struct
64 const char *name;
65 UINT32 flags;
66 } FS_DESCR;
68 static const FS_DESCR DRIVE_Filesystems[] =
70 { "unix", DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
71 { "msdos", DRIVE_SHORT_NAMES },
72 { "dos", DRIVE_SHORT_NAMES },
73 { "fat", DRIVE_SHORT_NAMES },
74 { "vfat", DRIVE_CASE_PRESERVING },
75 { "win95", DRIVE_CASE_PRESERVING },
76 { NULL, 0 }
80 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
81 static int DRIVE_CurDrive = -1;
83 static HTASK16 DRIVE_LastTask = 0;
86 /***********************************************************************
87 * DRIVE_GetDriveType
89 static DRIVETYPE DRIVE_GetDriveType( const char *name )
91 char buffer[20];
92 int i;
94 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
95 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
97 if (!lstrcmpi32A( buffer, DRIVE_Types[i] )) return (DRIVETYPE)i;
99 fprintf( stderr, "%s: unknown type '%s', defaulting to 'hd'.\n",
100 name, buffer );
101 return TYPE_HD;
105 /***********************************************************************
106 * DRIVE_GetFSFlags
108 static UINT32 DRIVE_GetFSFlags( const char *name, const char *value )
110 const FS_DESCR *descr;
112 for (descr = DRIVE_Filesystems; descr->name; descr++)
113 if (!lstrcmpi32A( value, descr->name )) return descr->flags;
114 fprintf( stderr, "%s: unknown filesystem type '%s', defaulting to 'unix'.\n",
115 name, value );
116 return DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING;
120 /***********************************************************************
121 * DRIVE_Init
123 int DRIVE_Init(void)
125 int i, len, count = 0;
126 char name[] = "Drive A";
127 char path[MAX_PATHNAME_LEN];
128 char buffer[20];
129 char *p;
130 DOSDRIVE *drive;
132 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
134 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
135 if (path[0])
137 p = path + strlen(path) - 1;
138 while ((p > path) && ((*p == '/') || (*p == '\\'))) *p-- = '\0';
139 if (strlen(path))
140 drive->root = xstrdup( path );
141 else
142 drive->root = xstrdup( "/" );
143 drive->dos_cwd = xstrdup( "" );
144 drive->unix_cwd = xstrdup( "" );
145 drive->type = DRIVE_GetDriveType( name );
146 drive->flags = 0;
148 /* Get the drive label */
149 PROFILE_GetWineIniString( name, "Label", name, drive->label, 12 );
150 if ((len = strlen(drive->label)) < 11)
152 /* Pad label with spaces */
153 memset( drive->label + len, ' ', 11 - len );
154 drive->label[12] = '\0';
157 /* Get the serial number */
158 PROFILE_GetWineIniString( name, "Serial", "12345678",
159 buffer, sizeof(buffer) );
160 drive->serial = strtoul( buffer, NULL, 16 );
162 /* Get the filesystem type */
163 PROFILE_GetWineIniString( name, "Filesystem", "unix",
164 buffer, sizeof(buffer) );
165 drive->flags = DRIVE_GetFSFlags( name, buffer );
167 /* Make the first hard disk the current drive */
168 if ((DRIVE_CurDrive == -1) && (drive->type == TYPE_HD))
169 DRIVE_CurDrive = i;
171 count++;
172 dprintf_dosfs( stddeb, "%s: path=%s type=%s label='%s' serial=%08lx flags=%08x\n",
173 name, path, DRIVE_Types[drive->type],
174 drive->label, drive->serial, drive->flags );
176 else dprintf_dosfs( stddeb, "%s: not defined\n", name );
179 if (!count)
181 fprintf( stderr, "Warning: no valid DOS drive found, check your configuration file.\n" );
182 /* Create a C drive pointing to Unix root dir */
183 DOSDrives[2].root = xstrdup( "/" );
184 DOSDrives[2].dos_cwd = xstrdup( "" );
185 DOSDrives[2].unix_cwd = xstrdup( "" );
186 strcpy( DOSDrives[2].label, "Drive C " );
187 DOSDrives[2].serial = 0x12345678;
188 DOSDrives[2].type = TYPE_HD;
189 DOSDrives[2].flags = 0;
190 DRIVE_CurDrive = 2;
193 /* Make sure the current drive is valid */
194 if (DRIVE_CurDrive == -1)
196 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
198 if (drive->root && !(drive->flags & DRIVE_DISABLED))
200 DRIVE_CurDrive = i;
201 break;
206 return 1;
210 /***********************************************************************
211 * DRIVE_IsValid
213 int DRIVE_IsValid( int drive )
215 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
216 return (DOSDrives[drive].root &&
217 !(DOSDrives[drive].flags & DRIVE_DISABLED));
221 /***********************************************************************
222 * DRIVE_GetCurrentDrive
224 int DRIVE_GetCurrentDrive(void)
226 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
227 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
228 return DRIVE_CurDrive;
232 /***********************************************************************
233 * DRIVE_SetCurrentDrive
235 int DRIVE_SetCurrentDrive( int drive )
237 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
238 if (!DRIVE_IsValid( drive ))
240 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
241 return 0;
243 dprintf_dosfs( stddeb, "DRIVE_SetCurrentDrive: %c:\n", 'A' + drive );
244 DRIVE_CurDrive = drive;
245 if (pTask) pTask->curdrive = drive | 0x80;
246 return 1;
250 /***********************************************************************
251 * DRIVE_FindDriveRoot
253 * Find a drive for which the root matches the begginning of the given path.
254 * This can be used to translate a Unix path into a drive + DOS path.
255 * Return value is the drive, or -1 on error. On success, path is modified
256 * to point to the beginning of the DOS path.
257 * FIXME: this only does a textual comparison of the path names, and won't
258 * work well in the presence of symbolic links.
260 int DRIVE_FindDriveRoot( const char **path )
262 int drive, rootdrive = -1;
263 const char *p1, *p2;
265 dprintf_dosfs( stddeb, "DRIVE_FindDriveRoot: searching '%s'\n", *path );
266 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
268 if (!DOSDrives[drive].root ||
269 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
270 p1 = *path;
271 p2 = DOSDrives[drive].root;
272 dprintf_dosfs( stddeb, "DRIVE_FindDriveRoot: checking %c: '%s'\n",
273 'A' + drive, p2 );
275 while (*p2 == '/') p2++;
276 if (!*p2)
278 rootdrive = drive;
279 continue; /* Look if there's a better match */
281 for (;;)
283 while ((*p1 == '\\') || (*p1 == '/')) p1++;
284 while (*p2 == '/') p2++;
285 while ((*p1 == *p2) && (*p2) && (*p2 != '/')) p1++, p2++;
286 if (!*p2)
288 if (IS_END_OF_NAME(*p1)) /* OK, found it */
290 *path = p1;
291 return drive;
294 else if (*p2 == '/')
296 if (IS_END_OF_NAME(*p1))
297 continue; /* Go to next path element */
299 break; /* No match, go to next drive */
302 return rootdrive;
306 /***********************************************************************
307 * DRIVE_GetRoot
309 const char * DRIVE_GetRoot( int drive )
311 if (!DRIVE_IsValid( drive )) return NULL;
312 return DOSDrives[drive].root;
316 /***********************************************************************
317 * DRIVE_GetDosCwd
319 const char * DRIVE_GetDosCwd( int drive )
321 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
322 if (!DRIVE_IsValid( drive )) return NULL;
324 /* Check if we need to change the directory to the new task. */
325 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
326 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
327 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
329 /* Perform the task-switch */
330 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
331 DRIVE_LastTask = GetCurrentTask();
333 return DOSDrives[drive].dos_cwd;
337 /***********************************************************************
338 * DRIVE_GetUnixCwd
340 const char * DRIVE_GetUnixCwd( int drive )
342 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
343 if (!DRIVE_IsValid( drive )) return NULL;
345 /* Check if we need to change the directory to the new task. */
346 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
347 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
348 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
350 /* Perform the task-switch */
351 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
352 DRIVE_LastTask = GetCurrentTask();
354 return DOSDrives[drive].unix_cwd;
358 /***********************************************************************
359 * DRIVE_GetLabel
361 const char * DRIVE_GetLabel( int drive )
363 if (!DRIVE_IsValid( drive )) return NULL;
364 return DOSDrives[drive].label;
368 /***********************************************************************
369 * DRIVE_GetSerialNumber
371 DWORD DRIVE_GetSerialNumber( int drive )
373 if (!DRIVE_IsValid( drive )) return 0;
374 return DOSDrives[drive].serial;
378 /***********************************************************************
379 * DRIVE_SetSerialNumber
381 int DRIVE_SetSerialNumber( int drive, DWORD serial )
383 if (!DRIVE_IsValid( drive )) return 0;
384 DOSDrives[drive].serial = serial;
385 return 1;
389 /***********************************************************************
390 * DRIVE_GetType
392 DRIVETYPE DRIVE_GetType( int drive )
394 if (!DRIVE_IsValid( drive )) return TYPE_INVALID;
395 return DOSDrives[drive].type;
399 /***********************************************************************
400 * DRIVE_GetFlags
402 UINT32 DRIVE_GetFlags( int drive )
404 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
405 return DOSDrives[drive].flags;
409 /***********************************************************************
410 * DRIVE_Chdir
412 int DRIVE_Chdir( int drive, const char *path )
414 char buffer[MAX_PATHNAME_LEN];
415 const char *unix_cwd, *dos_cwd;
416 BYTE attr;
417 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
419 dprintf_dosfs( stddeb, "DRIVE_Chdir(%c:,%s)\n", 'A' + drive, path );
420 strcpy( buffer, "A:" );
421 buffer[0] += drive;
422 lstrcpyn32A( buffer + 2, path, sizeof(buffer) - 2 );
424 if (!(unix_cwd = DOSFS_GetUnixFileName( buffer, TRUE ))) return 0;
425 if (!FILE_Stat( unix_cwd, &attr, NULL, NULL, NULL )) return 0;
426 if (!(attr & FA_DIRECTORY))
428 DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
429 return 0;
431 unix_cwd += strlen( DOSDrives[drive].root );
432 while (*unix_cwd == '/') unix_cwd++;
433 buffer[2] = '/';
434 lstrcpyn32A( buffer + 3, unix_cwd, sizeof(buffer) - 3 );
435 if (!(dos_cwd = DOSFS_GetDosTrueName( buffer, TRUE ))) return 0;
437 dprintf_dosfs( stddeb, "DRIVE_Chdir(%c:): unix_cwd=%s dos_cwd=%s\n",
438 'A' + drive, unix_cwd, dos_cwd + 3 );
440 free( DOSDrives[drive].dos_cwd );
441 free( DOSDrives[drive].unix_cwd );
442 DOSDrives[drive].dos_cwd = xstrdup( dos_cwd + 3 );
443 DOSDrives[drive].unix_cwd = xstrdup( unix_cwd );
445 if (pTask && (pTask->curdrive & 0x80) &&
446 ((pTask->curdrive & ~0x80) == drive))
448 lstrcpyn32A( pTask->curdir, dos_cwd + 2, sizeof(pTask->curdir) );
449 DRIVE_LastTask = GetCurrentTask();
451 return 1;
455 /***********************************************************************
456 * DRIVE_Disable
458 int DRIVE_Disable( int drive )
460 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
462 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
463 return 0;
465 DOSDrives[drive].flags |= DRIVE_DISABLED;
466 return 1;
470 /***********************************************************************
471 * DRIVE_Enable
473 int DRIVE_Enable( int drive )
475 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
477 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
478 return 0;
480 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
481 return 1;
485 /***********************************************************************
486 * DRIVE_GetFreeSpace
488 static int DRIVE_GetFreeSpace( int drive, DWORD *size, DWORD *available )
490 struct statfs info;
492 if (!DRIVE_IsValid(drive))
494 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
495 return 0;
498 #if defined(__svr4__) || defined(_SCO_DS)
499 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
500 #else
501 if (statfs( DOSDrives[drive].root, &info) < 0)
502 #endif
504 FILE_SetDosError();
505 fprintf(stderr,"dosfs: cannot do statfs(%s)\n", DOSDrives[drive].root);
506 return 0;
509 *size = info.f_bsize * info.f_blocks;
510 #if defined(__svr4__) || defined(_SCO_DS)
511 *available = info.f_bfree * info.f_bsize;
512 #else
513 *available = info.f_bavail * info.f_bsize;
514 #endif
515 return 1;
519 /***********************************************************************
520 * GetDiskFreeSpace16 (KERNEL.422)
522 BOOL16 GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
523 LPDWORD sector_bytes, LPDWORD free_clusters,
524 LPDWORD total_clusters )
526 return GetDiskFreeSpace32A( root, cluster_sectors, sector_bytes,
527 free_clusters, total_clusters );
531 /***********************************************************************
532 * GetDiskFreeSpaceA (KERNEL32.206)
534 BOOL32 GetDiskFreeSpace32A( LPCSTR root, LPDWORD cluster_sectors,
535 LPDWORD sector_bytes, LPDWORD free_clusters,
536 LPDWORD total_clusters )
538 int drive;
539 DWORD size,available;
541 if (!root) drive = DRIVE_GetCurrentDrive();
542 else
544 if ((root[1] != ':') || (root[2] != '\\'))
546 fprintf( stderr, "GetDiskFreeSpaceA: invalid root '%s'\n", root );
547 return FALSE;
549 drive = toupper(root[0]) - 'A';
551 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
553 *sector_bytes = 512;
554 size /= 512;
555 available /= 512;
556 *cluster_sectors = 1;
557 while (*cluster_sectors * 65530 < size) *cluster_sectors *= 2;
558 *free_clusters = available/ *cluster_sectors;
559 *total_clusters = size/ *cluster_sectors;
560 return TRUE;
564 /***********************************************************************
565 * GetDiskFreeSpaceW (KERNEL32.207)
567 BOOL32 GetDiskFreeSpace32W( LPCWSTR root, LPDWORD cluster_sectors,
568 LPDWORD sector_bytes, LPDWORD free_clusters,
569 LPDWORD total_clusters )
571 LPSTR xroot;
572 BOOL ret;
574 xroot = STRING32_DupUniToAnsi(root);
575 ret = GetDiskFreeSpace32A( xroot,cluster_sectors, sector_bytes,
576 free_clusters, total_clusters );
577 free( xroot );
578 return ret;
582 /***********************************************************************
583 * GetDriveType16 (KERNEL.136)
585 UINT16 GetDriveType16( UINT16 drive )
587 dprintf_dosfs( stddeb, "GetDriveType(%c:)\n", 'A' + drive );
588 switch(DRIVE_GetType(drive))
590 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
591 case TYPE_HD: return DRIVE_FIXED;
592 case TYPE_CDROM: return DRIVE_REMOTE;
593 case TYPE_NETWORK: return DRIVE_REMOTE;
594 case TYPE_INVALID:
595 default: return DRIVE_CANNOTDETERMINE;
600 /***********************************************************************
601 * GetDriveType32A (KERNEL32.208)
603 UINT32 GetDriveType32A( LPCSTR root )
605 dprintf_dosfs( stddeb, "GetDriveType32A(%s)\n", root );
606 if ((root[1] != ':') || (root[2] != '\\'))
608 fprintf( stderr, "GetDriveType32A: invalid root '%s'\n", root );
609 return DRIVE_DOESNOTEXIST;
611 switch(DRIVE_GetType(toupper(root[0]) - 'A'))
613 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
614 case TYPE_HD: return DRIVE_FIXED;
615 case TYPE_CDROM: return DRIVE_CDROM;
616 case TYPE_NETWORK: return DRIVE_REMOTE;
617 case TYPE_INVALID:
618 default: return DRIVE_CANNOTDETERMINE;
623 /***********************************************************************
624 * GetDriveType32W (KERNEL32.209)
626 UINT32 GetDriveType32W( LPCWSTR root )
628 LPSTR xpath=STRING32_DupUniToAnsi(root);
629 UINT32 ret;
631 ret = GetDriveType32A(xpath);
632 free(xpath);
633 return ret;
637 /***********************************************************************
638 * GetCurrentDirectory16 (KERNEL.411)
640 UINT16 GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
642 return (UINT16)GetCurrentDirectory32A( buflen, buf );
646 /***********************************************************************
647 * GetCurrentDirectory32A (KERNEL32.196)
649 * Returns "X:\\path\\etc\\".
651 UINT32 GetCurrentDirectory32A( UINT32 buflen, LPSTR buf )
653 char *pref = "A:\\";
654 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
655 if (!s)
657 *buf = '\0';
658 return 0;
660 lstrcpyn32A( buf, pref, 3 );
661 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
662 if (buflen >= 3) lstrcpyn32A( buf + 3, s, buflen - 3 );
663 return strlen(s) + 3; /* length of WHOLE current directory */
667 /***********************************************************************
668 * GetCurrentDirectory32W (KERNEL32.197)
670 UINT32 GetCurrentDirectory32W( UINT32 buflen, LPWSTR buf )
672 LPSTR xpath=(char*)xmalloc(buflen+1);
673 UINT32 ret;
675 ret = GetCurrentDirectory32A(buflen,xpath);
676 STRING32_AnsiToUni(buf,xpath);
677 free(xpath);
678 return ret;
682 /***********************************************************************
683 * SetCurrentDirectory (KERNEL.412)
685 BOOL32 SetCurrentDirectory( LPCSTR dir )
687 if (dir[0] && (dir[1]==':'))
689 int drive = tolower( *dir ) - 'a';
690 if (!DRIVE_IsValid(drive))
692 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
693 return 0;
695 dir += 2;
697 /* FIXME: what about empty strings? Add a \\ ? */
698 return DRIVE_Chdir( DRIVE_GetCurrentDrive(), dir );
702 /***********************************************************************
703 * GetLogicalDriveStrings32A (KERNEL32.231)
705 UINT32 GetLogicalDriveStrings32A( UINT32 len, LPSTR buffer )
707 int drive, count;
709 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
710 if (DRIVE_IsValid(drive)) count++;
711 if (count * 4 * sizeof(char) <= len)
713 LPSTR p = buffer;
714 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
715 if (DRIVE_IsValid(drive))
717 *p++ = 'a' + drive;
718 *p++ = ':';
719 *p++ = '\\';
720 *p++ = '\0';
722 *p = '\0';
724 return count * 4 * sizeof(char);
728 /***********************************************************************
729 * GetLogicalDriveStrings32W (KERNEL32.232)
731 UINT32 GetLogicalDriveStrings32W( UINT32 len, LPWSTR buffer )
733 int drive, count;
735 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
736 if (DRIVE_IsValid(drive)) count++;
737 if (count * 4 * sizeof(WCHAR) <= len)
739 LPWSTR p = buffer;
740 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
741 if (DRIVE_IsValid(drive))
743 *p++ = (WCHAR)('a' + drive);
744 *p++ = (WCHAR)':';
745 *p++ = (WCHAR)'\\';
746 *p++ = (WCHAR)'\0';
748 *p = (WCHAR)'\0';
750 return count * 4 * sizeof(WCHAR);
754 /***********************************************************************
755 * GetLogicalDrives (KERNEL32.233)
757 DWORD GetLogicalDrives(void)
759 DWORD ret = 0;
760 int drive;
762 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
763 if (DRIVE_IsValid(drive)) ret |= (1 << drive);
764 return ret;
768 /***********************************************************************
769 * GetVolumeInformation32A (KERNEL32.309)
771 BOOL32 GetVolumeInformation32A( LPCSTR root, LPSTR label, DWORD label_len,
772 DWORD *serial, DWORD *filename_len,
773 DWORD *flags, LPSTR fsname, DWORD fsname_len )
775 int drive;
777 /* FIXME, SetLastErrors missing */
779 if (!root) drive = DRIVE_GetCurrentDrive();
780 else
782 if ((root[1] != ':') || (root[2] != '\\'))
784 fprintf( stderr, "GetVolumeInformation: invalid root '%s'\n",root);
785 return FALSE;
787 drive = toupper(root[0]) - 'A';
789 if (!DRIVE_IsValid( drive )) return FALSE;
790 if (label) lstrcpyn32A( label, DOSDrives[drive].label, label_len );
791 if (serial) *serial = DOSDrives[drive].serial;
793 /* Set the filesystem information */
794 /* Note: we only emulate a FAT fs at the present */
796 if (filename_len) *filename_len = 12;
797 if (flags) *flags = 0;
798 if (fsname) lstrcpyn32A( fsname, "FAT", fsname_len );
799 return TRUE;
803 /***********************************************************************
804 * GetVolumeInformation32W (KERNEL32.310)
806 BOOL32 GetVolumeInformation32W( LPCWSTR root, LPWSTR label, DWORD label_len,
807 DWORD *serial, DWORD *filename_len,
808 DWORD *flags, LPWSTR fsname, DWORD fsname_len)
810 LPSTR xroot = STRING32_DupUniToAnsi(root);
811 LPSTR xvolname = (char*)xmalloc( label_len );
812 LPSTR xfsname = (char*)xmalloc( fsname_len );
813 BOOL32 ret = GetVolumeInformation32A( xroot, xvolname, label_len, serial,
814 filename_len, flags, xfsname,
815 fsname_len );
816 if (ret)
818 STRING32_AnsiToUni( label, xvolname );
819 STRING32_AnsiToUni( fsname, xfsname );
821 free(xroot);
822 free(xvolname);
823 free(xfsname);
824 return ret;