Use libwine and libwine_unicode directly from their build directory
[wine/multimedia.git] / files / drive.c
blob6ab76ae86f2185e9bc5da3b94f1609fba546399d
1 /*
2 * DOS drives handling functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
7 * Label & serial number read support.
8 * (c) 1999 Petr Tomasek <tomasek@etf.cuni.cz>
9 * (c) 2000 Andreas Mohr (changes)
13 #include "config.h"
15 #include <assert.h>
16 #include <ctype.h>
17 #include <string.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <unistd.h>
26 #ifdef HAVE_SYS_PARAM_H
27 # include <sys/param.h>
28 #endif
29 #ifdef STATFS_DEFINED_BY_SYS_VFS
30 # include <sys/vfs.h>
31 #else
32 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
33 # include <sys/mount.h>
34 # else
35 # ifdef STATFS_DEFINED_BY_SYS_STATFS
36 # include <sys/statfs.h>
37 # endif
38 # endif
39 #endif
41 #include "winbase.h"
42 #include "ntddk.h"
43 #include "wine/winbase16.h" /* for GetCurrentTask */
44 #include "winerror.h"
45 #include "drive.h"
46 #include "cdrom.h"
47 #include "file.h"
48 #include "heap.h"
49 #include "msdos.h"
50 #include "options.h"
51 #include "wine/port.h"
52 #include "task.h"
53 #include "debugtools.h"
55 DEFAULT_DEBUG_CHANNEL(dosfs);
56 DECLARE_DEBUG_CHANNEL(file);
58 typedef struct
60 char *root; /* root dir in Unix format without trailing / */
61 char *dos_cwd; /* cwd in DOS format without leading or trailing \ */
62 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
63 char *device; /* raw device path */
64 char label_conf[12]; /* drive label as cfg'd in wine.conf */
65 char label_read[12]; /* drive label as read from device */
66 DWORD serial_conf; /* drive serial number as cfg'd in wine.conf */
67 DRIVETYPE type; /* drive type */
68 UINT flags; /* drive flags */
69 dev_t dev; /* unix device number */
70 ino_t ino; /* unix inode number */
71 } DOSDRIVE;
74 static const char * const DRIVE_Types[] =
76 "floppy", /* TYPE_FLOPPY */
77 "hd", /* TYPE_HD */
78 "cdrom", /* TYPE_CDROM */
79 "network" /* TYPE_NETWORK */
83 /* Known filesystem types */
85 typedef struct
87 const char *name;
88 UINT flags;
89 } FS_DESCR;
91 static const FS_DESCR DRIVE_Filesystems[] =
93 { "unix", DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
94 { "msdos", DRIVE_SHORT_NAMES },
95 { "dos", DRIVE_SHORT_NAMES },
96 { "fat", DRIVE_SHORT_NAMES },
97 { "vfat", DRIVE_CASE_PRESERVING },
98 { "win95", DRIVE_CASE_PRESERVING },
99 { NULL, 0 }
103 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
104 static int DRIVE_CurDrive = -1;
106 static HTASK16 DRIVE_LastTask = 0;
109 /***********************************************************************
110 * DRIVE_GetDriveType
112 static DRIVETYPE DRIVE_GetDriveType( const char *name )
114 char buffer[20];
115 int i;
117 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
118 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
120 if (!strcasecmp( buffer, DRIVE_Types[i] )) return (DRIVETYPE)i;
122 MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
123 name, buffer );
124 return TYPE_HD;
128 /***********************************************************************
129 * DRIVE_GetFSFlags
131 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
133 const FS_DESCR *descr;
135 for (descr = DRIVE_Filesystems; descr->name; descr++)
136 if (!strcasecmp( value, descr->name )) return descr->flags;
137 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
138 name, value );
139 return DRIVE_CASE_PRESERVING;
143 /***********************************************************************
144 * DRIVE_Init
146 int DRIVE_Init(void)
148 int i, len, count = 0;
149 char name[] = "Drive A";
150 char drive_env[] = "=A:";
151 char path[MAX_PATHNAME_LEN];
152 char buffer[80];
153 struct stat drive_stat_buffer;
154 char *p;
155 DOSDRIVE *drive;
157 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
159 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
160 if (path[0])
162 p = path + strlen(path) - 1;
163 while ((p > path) && ((*p == '/') || (*p == '\\'))) *p-- = '\0';
164 if (!path[0]) strcpy( path, "/" );
166 if (stat( path, &drive_stat_buffer ))
168 MESSAGE("Could not stat %s, ignoring drive %c:\n", path, 'A' + i );
169 continue;
171 if (!S_ISDIR(drive_stat_buffer.st_mode))
173 MESSAGE("%s is not a directory, ignoring drive %c:\n",
174 path, 'A' + i );
175 continue;
178 drive->root = HEAP_strdupA( GetProcessHeap(), 0, path );
179 drive->dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
180 drive->unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
181 drive->type = DRIVE_GetDriveType( name );
182 drive->device = NULL;
183 drive->flags = 0;
184 drive->dev = drive_stat_buffer.st_dev;
185 drive->ino = drive_stat_buffer.st_ino;
187 /* Get the drive label */
188 PROFILE_GetWineIniString( name, "Label", name, drive->label_conf, 12 );
189 if ((len = strlen(drive->label_conf)) < 11)
191 /* Pad label with spaces */
192 memset( drive->label_conf + len, ' ', 11 - len );
193 drive->label_conf[11] = '\0';
196 /* Get the serial number */
197 PROFILE_GetWineIniString( name, "Serial", "12345678",
198 buffer, sizeof(buffer) );
199 drive->serial_conf = strtoul( buffer, NULL, 16 );
201 /* Get the filesystem type */
202 PROFILE_GetWineIniString( name, "Filesystem", "win95",
203 buffer, sizeof(buffer) );
204 drive->flags = DRIVE_GetFSFlags( name, buffer );
206 /* Get the device */
207 PROFILE_GetWineIniString( name, "Device", "",
208 buffer, sizeof(buffer) );
209 if (buffer[0])
211 drive->device = HEAP_strdupA( GetProcessHeap(), 0, buffer );
212 if (PROFILE_GetWineIniBool( name, "ReadVolInfo", 1))
213 drive->flags |= DRIVE_READ_VOL_INFO;
216 /* Get the FailReadOnly flag */
217 if (PROFILE_GetWineIniBool( name, "FailReadOnly", 0 ))
218 drive->flags |= DRIVE_FAIL_READ_ONLY;
220 /* Make the first hard disk the current drive */
221 if ((DRIVE_CurDrive == -1) && (drive->type == TYPE_HD))
222 DRIVE_CurDrive = i;
224 count++;
225 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
226 "flags=%08x dev=%x ino=%x\n",
227 name, path, DRIVE_Types[drive->type],
228 drive->label_conf, drive->serial_conf, drive->flags,
229 (int)drive->dev, (int)drive->ino );
231 else WARN("%s: not defined\n", name );
234 if (!count)
236 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
237 /* Create a C drive pointing to Unix root dir */
238 DOSDrives[2].root = HEAP_strdupA( GetProcessHeap(), 0, "/" );
239 DOSDrives[2].dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
240 DOSDrives[2].unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
241 strcpy( DOSDrives[2].label_conf, "Drive C " );
242 DOSDrives[2].serial_conf = 12345678;
243 DOSDrives[2].type = TYPE_HD;
244 DOSDrives[2].device = NULL;
245 DOSDrives[2].flags = 0;
246 DRIVE_CurDrive = 2;
249 /* Make sure the current drive is valid */
250 if (DRIVE_CurDrive == -1)
252 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
254 if (drive->root && !(drive->flags & DRIVE_DISABLED))
256 DRIVE_CurDrive = i;
257 break;
262 /* get current working directory info for all drives */
263 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
265 if (!GetEnvironmentVariableA(drive_env, path, sizeof(path))) continue;
266 /* sanity check */
267 if (toupper(path[0]) != drive_env[1] || path[1] != ':') continue;
268 DRIVE_Chdir( i, path + 2 );
270 return 1;
274 /***********************************************************************
275 * DRIVE_IsValid
277 int DRIVE_IsValid( int drive )
279 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
280 return (DOSDrives[drive].root &&
281 !(DOSDrives[drive].flags & DRIVE_DISABLED));
285 /***********************************************************************
286 * DRIVE_GetCurrentDrive
288 int DRIVE_GetCurrentDrive(void)
290 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
291 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
292 return DRIVE_CurDrive;
296 /***********************************************************************
297 * DRIVE_SetCurrentDrive
299 int DRIVE_SetCurrentDrive( int drive )
301 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
302 if (!DRIVE_IsValid( drive ))
304 SetLastError( ERROR_INVALID_DRIVE );
305 return 0;
307 TRACE("%c:\n", 'A' + drive );
308 DRIVE_CurDrive = drive;
309 if (pTask) pTask->curdrive = drive | 0x80;
310 chdir(DRIVE_GetUnixCwd(drive));
311 return 1;
315 /***********************************************************************
316 * DRIVE_FindDriveRoot
318 * Find a drive for which the root matches the beginning of the given path.
319 * This can be used to translate a Unix path into a drive + DOS path.
320 * Return value is the drive, or -1 on error. On success, path is modified
321 * to point to the beginning of the DOS path.
323 int DRIVE_FindDriveRoot( const char **path )
325 /* idea: check at all '/' positions.
326 * If the device and inode of that path is identical with the
327 * device and inode of the current drive then we found a solution.
328 * If there is another drive pointing to a deeper position in
329 * the file tree, we want to find that one, not the earlier solution.
331 int drive, rootdrive = -1;
332 char buffer[MAX_PATHNAME_LEN];
333 char *next = buffer;
334 const char *p = *path;
335 struct stat st;
337 strcpy( buffer, "/" );
338 for (;;)
340 if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
342 /* Find the drive */
344 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
346 if (!DOSDrives[drive].root ||
347 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
349 if ((DOSDrives[drive].dev == st.st_dev) &&
350 (DOSDrives[drive].ino == st.st_ino))
352 rootdrive = drive;
353 *path = p;
354 break;
358 /* Get the next path component */
360 *next++ = '/';
361 while ((*p == '/') || (*p == '\\')) p++;
362 if (!*p) break;
363 while (!IS_END_OF_NAME(*p)) *next++ = *p++;
364 *next = 0;
366 *next = 0;
368 if (rootdrive != -1)
369 TRACE("%s -> drive %c:, root='%s', name='%s'\n",
370 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, *path );
371 return rootdrive;
375 /***********************************************************************
376 * DRIVE_GetRoot
378 const char * DRIVE_GetRoot( int drive )
380 if (!DRIVE_IsValid( drive )) return NULL;
381 return DOSDrives[drive].root;
385 /***********************************************************************
386 * DRIVE_GetDosCwd
388 const char * DRIVE_GetDosCwd( int drive )
390 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
391 if (!DRIVE_IsValid( drive )) return NULL;
393 /* Check if we need to change the directory to the new task. */
394 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
395 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
396 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
398 /* Perform the task-switch */
399 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
400 DRIVE_LastTask = GetCurrentTask();
402 return DOSDrives[drive].dos_cwd;
406 /***********************************************************************
407 * DRIVE_GetUnixCwd
409 const char * DRIVE_GetUnixCwd( int drive )
411 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
412 if (!DRIVE_IsValid( drive )) return NULL;
414 /* Check if we need to change the directory to the new task. */
415 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
416 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
417 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
419 /* Perform the task-switch */
420 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
421 DRIVE_LastTask = GetCurrentTask();
423 return DOSDrives[drive].unix_cwd;
427 /***********************************************************************
428 * DRIVE_GetDevice
430 const char * DRIVE_GetDevice( int drive )
432 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
436 /***********************************************************************
437 * DRIVE_ReadSuperblock
439 * NOTE
440 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
441 * to check, that they are writing on a FAT filesystem !
443 int DRIVE_ReadSuperblock (int drive, char * buff)
445 #define DRIVE_SUPER 96
446 int fd;
447 off_t offs;
449 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
450 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
452 struct stat st;
453 if (!DOSDrives[drive].device)
454 ERR("No device configured for drive %c: !\n", 'A'+drive);
455 else
456 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
457 (stat(DOSDrives[drive].device, &st)) ?
458 "not available or symlink not valid ?" : "no permission");
459 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
460 PROFILE_UsageWineIni();
461 return -1;
464 switch(DOSDrives[drive].type)
466 case TYPE_FLOPPY:
467 case TYPE_HD:
468 offs = 0;
469 break;
470 case TYPE_CDROM:
471 offs = CDROM_Data_FindBestVoldesc(fd);
472 break;
473 default:
474 offs = 0;
475 break;
478 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs)) return -4;
479 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER) return -2;
481 switch(DOSDrives[drive].type)
483 case TYPE_FLOPPY:
484 case TYPE_HD:
485 if ((buff[0x26]!=0x29) || /* Check for FAT present */
486 /* FIXME: do really all FAT have their name beginning with
487 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
488 memcmp( buff+0x36,"FAT",3))
490 ERR("The filesystem is not FAT !! (device=%s)\n",
491 DOSDrives[drive].device);
492 return -3;
494 break;
495 case TYPE_CDROM:
496 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
497 return -3;
498 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
499 break;
500 default:
501 return -3;
502 break;
505 return close(fd);
509 /***********************************************************************
510 * DRIVE_WriteSuperblockEntry
512 * NOTE
513 * We are writing as little as possible (ie. not the whole SuperBlock)
514 * not to interfere with kernel. The drive can be mounted !
516 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
518 int fd;
520 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
522 ERR("Cannot open the device %s (for writing)\n",
523 DOSDrives[drive].device);
524 return -1;
526 if (lseek(fd,ofs,SEEK_SET)!=ofs)
528 ERR("lseek failed on device %s !\n",
529 DOSDrives[drive].device);
530 close(fd);
531 return -2;
533 if (write(fd,buff,len)!=len)
535 close(fd);
536 ERR("Cannot write on %s !\n",
537 DOSDrives[drive].device);
538 return -3;
540 return close (fd);
545 /***********************************************************************
546 * DRIVE_GetLabel
548 const char * DRIVE_GetLabel( int drive )
550 int read = 0;
551 char buff[DRIVE_SUPER];
552 int offs = -1;
554 if (!DRIVE_IsValid( drive )) return NULL;
555 if (DRIVE_GetType(drive) == TYPE_CDROM)
557 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
559 else
560 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
562 if (DRIVE_ReadSuperblock(drive,(char *) buff))
563 ERR("Invalid or unreadable superblock on %s (%c:).\n",
564 DOSDrives[drive].device, (char)(drive+'A'));
565 else {
566 if (DOSDrives[drive].type == TYPE_FLOPPY ||
567 DOSDrives[drive].type == TYPE_HD)
568 offs = 0x2b;
570 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
571 if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
572 DOSDrives[drive].label_read[11]='\0';
573 read = 1;
577 return (read) ?
578 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
582 /***********************************************************************
583 * DRIVE_GetSerialNumber
585 DWORD DRIVE_GetSerialNumber( int drive )
587 DWORD serial = 0;
588 char buff[DRIVE_SUPER];
590 if (!DRIVE_IsValid( drive )) return 0;
592 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
594 switch(DOSDrives[drive].type)
596 case TYPE_FLOPPY:
597 case TYPE_HD:
598 if (DRIVE_ReadSuperblock(drive,(char *) buff))
599 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
600 " Maybe not FAT?\n" ,
601 DOSDrives[drive].device, 'A'+drive);
602 else
603 serial = *((DWORD*)(buff+0x27));
604 break;
605 case TYPE_CDROM:
606 serial = CDROM_GetSerial(drive);
607 break;
608 default:
609 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
613 return (serial) ? serial : DOSDrives[drive].serial_conf;
617 /***********************************************************************
618 * DRIVE_SetSerialNumber
620 int DRIVE_SetSerialNumber( int drive, DWORD serial )
622 char buff[DRIVE_SUPER];
624 if (!DRIVE_IsValid( drive )) return 0;
626 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
628 if ((DOSDrives[drive].type != TYPE_FLOPPY) &&
629 (DOSDrives[drive].type != TYPE_HD)) return 0;
630 /* check, if the drive has a FAT filesystem */
631 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
632 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
633 return 1;
636 if (DOSDrives[drive].type == TYPE_CDROM) return 0;
637 DOSDrives[drive].serial_conf = serial;
638 return 1;
642 /***********************************************************************
643 * DRIVE_GetType
645 DRIVETYPE DRIVE_GetType( int drive )
647 if (!DRIVE_IsValid( drive )) return TYPE_INVALID;
648 return DOSDrives[drive].type;
652 /***********************************************************************
653 * DRIVE_GetFlags
655 UINT DRIVE_GetFlags( int drive )
657 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
658 return DOSDrives[drive].flags;
662 /***********************************************************************
663 * DRIVE_Chdir
665 int DRIVE_Chdir( int drive, const char *path )
667 DOS_FULL_NAME full_name;
668 char buffer[MAX_PATHNAME_LEN];
669 LPSTR unix_cwd;
670 BY_HANDLE_FILE_INFORMATION info;
671 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
673 strcpy( buffer, "A:" );
674 buffer[0] += drive;
675 TRACE("(%s,%s)\n", buffer, path );
676 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
678 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
679 if (!FILE_Stat( full_name.long_name, &info )) return 0;
680 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
682 SetLastError( ERROR_FILE_NOT_FOUND );
683 return 0;
685 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
686 while (*unix_cwd == '/') unix_cwd++;
688 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
689 'A' + drive, unix_cwd, full_name.short_name + 3 );
691 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
692 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
693 DOSDrives[drive].dos_cwd = HEAP_strdupA( GetProcessHeap(), 0,
694 full_name.short_name + 3 );
695 DOSDrives[drive].unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, unix_cwd );
697 if (pTask && (pTask->curdrive & 0x80) &&
698 ((pTask->curdrive & ~0x80) == drive))
700 lstrcpynA( pTask->curdir, full_name.short_name + 2,
701 sizeof(pTask->curdir) );
702 DRIVE_LastTask = GetCurrentTask();
703 chdir(unix_cwd); /* Only change if on current drive */
705 return 1;
709 /***********************************************************************
710 * DRIVE_Disable
712 int DRIVE_Disable( int drive )
714 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
716 SetLastError( ERROR_INVALID_DRIVE );
717 return 0;
719 DOSDrives[drive].flags |= DRIVE_DISABLED;
720 return 1;
724 /***********************************************************************
725 * DRIVE_Enable
727 int DRIVE_Enable( int drive )
729 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
731 SetLastError( ERROR_INVALID_DRIVE );
732 return 0;
734 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
735 return 1;
739 /***********************************************************************
740 * DRIVE_SetLogicalMapping
742 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
744 /* If new_drive is already valid, do nothing and return 0
745 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
747 DOSDRIVE *old, *new;
749 old = DOSDrives + existing_drive;
750 new = DOSDrives + new_drive;
752 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
753 !old->root ||
754 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
756 SetLastError( ERROR_INVALID_DRIVE );
757 return 0;
760 if ( new->root )
762 TRACE("Can't map drive %c: to already existing drive %c:\n",
763 'A' + existing_drive, 'A' + new_drive );
764 /* it is already mapped there, so return success */
765 if (!strcmp(old->root,new->root))
766 return 1;
767 return 0;
770 new->root = HEAP_strdupA( GetProcessHeap(), 0, old->root );
771 new->dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->dos_cwd );
772 new->unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->unix_cwd );
773 new->device = HEAP_strdupA( GetProcessHeap(), 0, old->device );
774 memcpy ( new->label_conf, old->label_conf, 12 );
775 memcpy ( new->label_read, old->label_read, 12 );
776 new->serial_conf = old->serial_conf;
777 new->type = old->type;
778 new->flags = old->flags;
779 new->dev = old->dev;
780 new->ino = old->ino;
782 TRACE("Drive %c: is now equal to drive %c:\n",
783 'A' + new_drive, 'A' + existing_drive );
785 return 1;
789 /***********************************************************************
790 * DRIVE_OpenDevice
792 * Open the drive raw device and return a Unix fd (or -1 on error).
794 int DRIVE_OpenDevice( int drive, int flags )
796 if (!DRIVE_IsValid( drive )) return -1;
797 return open( DOSDrives[drive].device, flags );
801 /***********************************************************************
802 * DRIVE_RawRead
804 * Read raw sectors from a device
806 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
808 int fd;
810 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
812 lseek( fd, begin * 512, SEEK_SET );
813 /* FIXME: check errors */
814 read( fd, dataptr, nr_sect * 512 );
815 close( fd );
817 else
819 memset(dataptr, 0, nr_sect * 512);
820 if (fake_success)
822 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
823 if (begin == 1) *dataptr = 0xf8;
825 else
826 return 0;
828 return 1;
832 /***********************************************************************
833 * DRIVE_RawWrite
835 * Write raw sectors to a device
837 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
839 int fd;
841 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
843 lseek( fd, begin * 512, SEEK_SET );
844 /* FIXME: check errors */
845 write( fd, dataptr, nr_sect * 512 );
846 close( fd );
848 else
849 if (!(fake_success))
850 return 0;
852 return 1;
856 /***********************************************************************
857 * DRIVE_GetFreeSpace
859 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
860 PULARGE_INTEGER available )
862 struct statfs info;
864 if (!DRIVE_IsValid(drive))
866 SetLastError( ERROR_INVALID_DRIVE );
867 return 0;
870 /* FIXME: add autoconf check for this */
871 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
872 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
873 #else
874 if (statfs( DOSDrives[drive].root, &info) < 0)
875 #endif
877 FILE_SetDosError();
878 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
879 return 0;
882 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
883 #ifdef STATFS_HAS_BAVAIL
884 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
885 #else
886 # ifdef STATFS_HAS_BFREE
887 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
888 # else
889 # error "statfs has no bfree/bavail member!"
890 # endif
891 #endif
892 if (DRIVE_GetType(drive) == TYPE_CDROM)
893 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
894 available->QuadPart = 0;
896 return 1;
899 /***********************************************************************
900 * DRIVE_GetCurrentDirectory
901 * Returns "X:\\path\\etc\\".
903 * Despite the API description, return required length including the
904 * terminating null when buffer too small. This is the real behaviour.
907 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
909 UINT ret;
910 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
912 assert(s);
913 ret = strlen(s) + 3; /* length of WHOLE current directory */
914 if (ret >= buflen) return ret + 1;
915 lstrcpynA( buf, "A:\\", min( 4, buflen ) );
916 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
917 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
918 return ret;
922 /***********************************************************************
923 * DRIVE_BuildEnv
925 * Build the environment array containing the drives current directories.
926 * Resulting pointer must be freed with HeapFree.
928 char *DRIVE_BuildEnv(void)
930 int i, length = 0;
931 const char *cwd[MAX_DOS_DRIVES];
932 char *env, *p;
934 for (i = 0; i < MAX_DOS_DRIVES; i++)
936 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0]) length += strlen(cwd[i]) + 8;
938 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
939 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
941 if (cwd[i] && cwd[i][0])
942 p += sprintf( p, "=%c:=%c:\\%s", 'A'+i, 'A'+i, cwd[i] ) + 1;
944 *p = 0;
945 return env;
949 /***********************************************************************
950 * GetDiskFreeSpace16 (KERNEL.422)
952 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
953 LPDWORD sector_bytes, LPDWORD free_clusters,
954 LPDWORD total_clusters )
956 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
957 free_clusters, total_clusters );
961 /***********************************************************************
962 * GetDiskFreeSpaceA (KERNEL32.206)
964 * Fails if expression resulting from current drive's dir and "root"
965 * is not a root dir of the target drive.
967 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
968 * if the corresponding info is unneeded.
970 * FIXME: needs to support UNC names from Win95 OSR2 on.
972 * Behaviour under Win95a:
973 * CurrDir root result
974 * "E:\\TEST" "E:" FALSE
975 * "E:\\" "E:" TRUE
976 * "E:\\" "E" FALSE
977 * "E:\\" "\\" TRUE
978 * "E:\\TEST" "\\" TRUE
979 * "E:\\TEST" ":\\" FALSE
980 * "E:\\TEST" "E:\\" TRUE
981 * "E:\\TEST" "" FALSE
982 * "E:\\" "" FALSE (!)
983 * "E:\\" 0x0 TRUE
984 * "E:\\TEST" 0x0 TRUE (!)
985 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
986 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
988 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
989 LPDWORD sector_bytes, LPDWORD free_clusters,
990 LPDWORD total_clusters )
992 int drive, sec_size;
993 ULARGE_INTEGER size,available;
994 LPCSTR path;
995 DWORD cluster_sec;
997 if ((!root) || (strcmp(root,"\\") == 0))
998 drive = DRIVE_GetCurrentDrive();
999 else
1000 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
1002 drive = toupper(root[0]) - 'A';
1003 path = &root[2];
1004 if (path[0] == '\0')
1005 path = DRIVE_GetDosCwd(drive);
1006 else
1007 if (path[0] == '\\')
1008 path++;
1009 if (strlen(path)) /* oops, we are in a subdir */
1010 return FALSE;
1012 else
1013 return FALSE;
1015 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1017 /* Cap the size and available at 2GB as per specs. */
1018 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1020 size.s.HighPart = 0;
1021 size.s.LowPart = 0x7fffffff;
1023 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1025 available.s.HighPart =0;
1026 available.s.LowPart = 0x7fffffff;
1028 sec_size = (DRIVE_GetType(drive)==TYPE_CDROM) ? 2048 : 512;
1029 size.s.LowPart /= sec_size;
1030 available.s.LowPart /= sec_size;
1031 /* fixme: probably have to adjust those variables too for CDFS */
1032 cluster_sec = 1;
1033 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1035 if (cluster_sectors)
1036 *cluster_sectors = cluster_sec;
1037 if (sector_bytes)
1038 *sector_bytes = sec_size;
1039 if (free_clusters)
1040 *free_clusters = available.s.LowPart / cluster_sec;
1041 if (total_clusters)
1042 *total_clusters = size.s.LowPart / cluster_sec;
1043 return TRUE;
1047 /***********************************************************************
1048 * GetDiskFreeSpaceW (KERNEL32.207)
1050 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1051 LPDWORD sector_bytes, LPDWORD free_clusters,
1052 LPDWORD total_clusters )
1054 LPSTR xroot;
1055 BOOL ret;
1057 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1058 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
1059 free_clusters, total_clusters );
1060 HeapFree( GetProcessHeap(), 0, xroot );
1061 return ret;
1065 /***********************************************************************
1066 * GetDiskFreeSpaceExA (KERNEL32.871)
1068 * This function is used to aquire the size of the available and
1069 * total space on a logical volume.
1071 * RETURNS
1073 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1074 * detailed error information.
1077 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1078 PULARGE_INTEGER avail,
1079 PULARGE_INTEGER total,
1080 PULARGE_INTEGER totalfree)
1082 int drive;
1083 ULARGE_INTEGER size,available;
1085 if (!root) drive = DRIVE_GetCurrentDrive();
1086 else
1088 if ((root[1]) && ((root[1] != ':') || (root[2] != '\\')))
1090 FIXME("there are valid root names which are not supported yet\n");
1091 /* ..like UNC names, for instance. */
1093 WARN("invalid root '%s'\n", root );
1094 return FALSE;
1096 drive = toupper(root[0]) - 'A';
1099 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1101 if (total)
1103 total->s.HighPart = size.s.HighPart;
1104 total->s.LowPart = size.s.LowPart;
1107 if (totalfree)
1109 totalfree->s.HighPart = available.s.HighPart;
1110 totalfree->s.LowPart = available.s.LowPart;
1113 if (avail)
1115 if (FIXME_ON(dosfs))
1117 /* On Windows2000, we need to check the disk quota
1118 allocated for the user owning the calling process. We
1119 don't want to be more obtrusive than necessary with the
1120 FIXME messages, so don't print the FIXME unless Wine is
1121 actually masquerading as Windows2000. */
1123 OSVERSIONINFOA ovi;
1124 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1125 if (GetVersionExA(&ovi))
1127 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1128 FIXME("no per-user quota support yet\n");
1132 /* Quick hack, should eventually be fixed to work 100% with
1133 Windows2000 (see comment above). */
1134 avail->s.HighPart = available.s.HighPart;
1135 avail->s.LowPart = available.s.LowPart;
1138 return TRUE;
1141 /***********************************************************************
1142 * GetDiskFreeSpaceExW (KERNEL32.873)
1144 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1145 PULARGE_INTEGER total,
1146 PULARGE_INTEGER totalfree)
1148 LPSTR xroot;
1149 BOOL ret;
1151 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1152 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1153 HeapFree( GetProcessHeap(), 0, xroot );
1154 return ret;
1157 /***********************************************************************
1158 * GetDriveType16 (KERNEL.136)
1159 * This function returns the type of a drive in Win16.
1160 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1161 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1162 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1163 * do any pseudo-clever changes.
1165 * RETURNS
1166 * drivetype DRIVE_xxx
1168 UINT16 WINAPI GetDriveType16(
1169 UINT16 drive /* [in] number (NOT letter) of drive */
1171 TRACE("(%c:)\n", 'A' + drive );
1172 switch(DRIVE_GetType(drive))
1174 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
1175 case TYPE_HD: return DRIVE_FIXED;
1176 case TYPE_CDROM: return DRIVE_REMOTE;
1177 case TYPE_NETWORK: return DRIVE_REMOTE;
1178 case TYPE_INVALID:
1179 default: return DRIVE_CANNOTDETERMINE;
1184 /***********************************************************************
1185 * GetDriveTypeA (KERNEL32.208)
1187 * Returns the type of the disk drive specified. If root is NULL the
1188 * root of the current directory is used.
1190 * RETURNS
1192 * Type of drive (from Win32 SDK):
1194 * DRIVE_UNKNOWN unable to find out anything about the drive
1195 * DRIVE_NO_ROOT_DIR nonexistent root dir
1196 * DRIVE_REMOVABLE the disk can be removed from the machine
1197 * DRIVE_FIXED the disk can not be removed from the machine
1198 * DRIVE_REMOTE network disk
1199 * DRIVE_CDROM CDROM drive
1200 * DRIVE_RAMDISK virtual disk in RAM
1202 * DRIVE_DOESNOTEXIST FIXME Not valid return value
1203 * DRIVE_CANNOTDETERMINE FIXME Not valid return value
1205 * BUGS
1207 * Currently returns DRIVE_DOESNOTEXIST and DRIVE_CANNOTDETERMINE
1208 * when it really should return DRIVE_NO_ROOT_DIR and DRIVE_UNKNOWN.
1209 * Why were the former defines used?
1211 * DRIVE_RAMDISK is unsupported.
1213 UINT WINAPI GetDriveTypeA(LPCSTR root) /* [in] String describing drive */
1215 int drive;
1216 TRACE("(%s)\n", debugstr_a(root));
1218 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1219 else
1221 if ((root[1]) && (root[1] != ':'))
1223 WARN("invalid root '%s'\n", debugstr_a(root));
1224 return DRIVE_DOESNOTEXIST;
1226 drive = toupper(root[0]) - 'A';
1228 switch(DRIVE_GetType(drive))
1230 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
1231 case TYPE_HD: return DRIVE_FIXED;
1232 case TYPE_CDROM: return DRIVE_CDROM;
1233 case TYPE_NETWORK: return DRIVE_REMOTE;
1234 case TYPE_INVALID: return DRIVE_DOESNOTEXIST;
1235 default: return DRIVE_CANNOTDETERMINE;
1240 /***********************************************************************
1241 * GetDriveTypeW (KERNEL32.209)
1243 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1245 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1246 UINT ret = GetDriveTypeA( xpath );
1247 HeapFree( GetProcessHeap(), 0, xpath );
1248 return ret;
1252 /***********************************************************************
1253 * GetCurrentDirectory16 (KERNEL.411)
1255 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1257 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1261 /***********************************************************************
1262 * GetCurrentDirectoryA (KERNEL32.196)
1264 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1266 UINT ret;
1267 char longname[MAX_PATHNAME_LEN];
1268 char shortname[MAX_PATHNAME_LEN];
1269 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1270 if ( ret > MAX_PATHNAME_LEN ) {
1271 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1272 return ret;
1274 GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1275 ret = strlen( longname ) + 1;
1276 if (ret > buflen) return ret;
1277 strcpy(buf, longname);
1278 return ret - 1;
1281 /***********************************************************************
1282 * GetCurrentDirectoryW (KERNEL32.197)
1284 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1286 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1287 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1288 if (ret < buflen) ret = MultiByteToWideChar( CP_ACP, 0, xpath, -1, buf, buflen ) - 1;
1289 HeapFree( GetProcessHeap(), 0, xpath );
1290 return ret;
1294 /***********************************************************************
1295 * SetCurrentDirectory (KERNEL.412)
1297 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1299 return SetCurrentDirectoryA( dir );
1303 /***********************************************************************
1304 * SetCurrentDirectoryA (KERNEL32.479)
1306 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1308 int drive, olddrive = DRIVE_GetCurrentDrive();
1310 if (!dir) {
1311 ERR_(file)("(NULL)!\n");
1312 return FALSE;
1314 if (dir[0] && (dir[1]==':'))
1316 drive = toupper( *dir ) - 'A';
1317 dir += 2;
1319 else
1320 drive = olddrive;
1322 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1323 sets pTask->curdir only if pTask->curdrive is drive */
1324 if (!(DRIVE_SetCurrentDrive( drive )))
1325 return FALSE;
1326 /* FIXME: what about empty strings? Add a \\ ? */
1327 if (!DRIVE_Chdir( drive, dir )) {
1328 DRIVE_SetCurrentDrive(olddrive);
1329 return FALSE;
1331 return TRUE;
1335 /***********************************************************************
1336 * SetCurrentDirectoryW (KERNEL32.480)
1338 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1340 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1341 BOOL res = SetCurrentDirectoryA( dir );
1342 HeapFree( GetProcessHeap(), 0, dir );
1343 return res;
1347 /***********************************************************************
1348 * GetLogicalDriveStringsA (KERNEL32.231)
1350 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1352 int drive, count;
1354 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1355 if (DRIVE_IsValid(drive)) count++;
1356 if ((count * 4) + 1 <= len)
1358 LPSTR p = buffer;
1359 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1360 if (DRIVE_IsValid(drive))
1362 *p++ = 'a' + drive;
1363 *p++ = ':';
1364 *p++ = '\\';
1365 *p++ = '\0';
1367 *p = '\0';
1368 return count * 4;
1370 else
1371 return (count * 4) + 1; /* account for terminating null */
1372 /* The API tells about these different return values */
1376 /***********************************************************************
1377 * GetLogicalDriveStringsW (KERNEL32.232)
1379 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1381 int drive, count;
1383 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1384 if (DRIVE_IsValid(drive)) count++;
1385 if (count * 4 * sizeof(WCHAR) <= len)
1387 LPWSTR p = buffer;
1388 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1389 if (DRIVE_IsValid(drive))
1391 *p++ = (WCHAR)('a' + drive);
1392 *p++ = (WCHAR)':';
1393 *p++ = (WCHAR)'\\';
1394 *p++ = (WCHAR)'\0';
1396 *p = (WCHAR)'\0';
1398 return count * 4 * sizeof(WCHAR);
1402 /***********************************************************************
1403 * GetLogicalDrives (KERNEL32.233)
1405 DWORD WINAPI GetLogicalDrives(void)
1407 DWORD ret = 0;
1408 int drive;
1410 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1412 if ( (DRIVE_IsValid(drive)) ||
1413 (DOSDrives[drive].type == TYPE_CDROM)) /* audio CD is also valid */
1414 ret |= (1 << drive);
1416 return ret;
1420 /***********************************************************************
1421 * GetVolumeInformationA (KERNEL32.309)
1423 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1424 DWORD label_len, DWORD *serial,
1425 DWORD *filename_len, DWORD *flags,
1426 LPSTR fsname, DWORD fsname_len )
1428 int drive;
1429 char *cp;
1431 /* FIXME, SetLastError()s missing */
1433 if (!root) drive = DRIVE_GetCurrentDrive();
1434 else
1436 if ((root[1]) && (root[1] != ':'))
1438 WARN("invalid root '%s'\n",root);
1439 return FALSE;
1441 drive = toupper(root[0]) - 'A';
1443 if (!DRIVE_IsValid( drive )) return FALSE;
1444 if (label)
1446 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1447 for (cp = label; *cp; cp++);
1448 while (cp != label && *(cp-1) == ' ') cp--;
1449 *cp = '\0';
1451 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1453 /* Set the filesystem information */
1454 /* Note: we only emulate a FAT fs at present */
1456 if (filename_len) {
1457 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1458 *filename_len = 12;
1459 else
1460 *filename_len = 255;
1462 if (flags)
1464 *flags=0;
1465 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1466 *flags|=FS_CASE_SENSITIVE;
1467 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1468 *flags|=FS_CASE_IS_PRESERVED;
1470 if (fsname) {
1471 /* Diablo checks that return code ... */
1472 if (DRIVE_GetType(drive)==TYPE_CDROM)
1473 lstrcpynA( fsname, "CDFS", fsname_len );
1474 else
1475 lstrcpynA( fsname, "FAT", fsname_len );
1477 return TRUE;
1481 /***********************************************************************
1482 * GetVolumeInformationW (KERNEL32.310)
1484 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1485 DWORD label_len, DWORD *serial,
1486 DWORD *filename_len, DWORD *flags,
1487 LPWSTR fsname, DWORD fsname_len )
1489 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1490 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1491 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1492 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1493 filename_len, flags, xfsname,
1494 fsname_len );
1495 if (ret)
1497 if (label) MultiByteToWideChar( CP_ACP, 0, xvolname, -1, label, label_len );
1498 if (fsname) MultiByteToWideChar( CP_ACP, 0, xfsname, -1, fsname, fsname_len );
1500 HeapFree( GetProcessHeap(), 0, xroot );
1501 HeapFree( GetProcessHeap(), 0, xvolname );
1502 HeapFree( GetProcessHeap(), 0, xfsname );
1503 return ret;
1506 /***********************************************************************
1507 * SetVolumeLabelA (KERNEL32.675)
1509 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1511 int drive;
1513 /* FIXME, SetLastErrors missing */
1515 if (!root) drive = DRIVE_GetCurrentDrive();
1516 else
1518 if ((root[1]) && (root[1] != ':'))
1520 WARN("invalid root '%s'\n",root);
1521 return FALSE;
1523 drive = toupper(root[0]) - 'A';
1525 if (!DRIVE_IsValid( drive )) return FALSE;
1527 /* some copy protection stuff check this */
1528 if (DRIVE_GetType( drive ) == TYPE_CDROM) return FALSE;
1530 FIXME("(%s,%s),stub!\n", root, volname);
1531 return TRUE;
1534 /***********************************************************************
1535 * SetVolumeLabelW (KERNEL32.676)
1537 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1539 LPSTR xroot, xvol;
1540 BOOL ret;
1542 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1543 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1544 ret = SetVolumeLabelA( xroot, xvol );
1545 HeapFree( GetProcessHeap(), 0, xroot );
1546 HeapFree( GetProcessHeap(), 0, xvol );
1547 return ret;