Use the system local time if lpTime=NULL.
[wine/hacks.git] / files / drive.c
blob3480c9afdf6c1f1a6a8ab32f0c9dfbca31a24f1e
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 <stdlib.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <unistd.h>
25 #ifdef HAVE_SYS_PARAM_H
26 # include <sys/param.h>
27 #endif
28 #ifdef STATFS_DEFINED_BY_SYS_VFS
29 # include <sys/vfs.h>
30 #else
31 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
32 # include <sys/mount.h>
33 # else
34 # ifdef STATFS_DEFINED_BY_SYS_STATFS
35 # include <sys/statfs.h>
36 # endif
37 # endif
38 #endif
40 #include "winbase.h"
41 #include "wine/winbase16.h" /* for GetCurrentTask */
42 #include "wine/winestring.h" /* for lstrcpyAtoW */
43 #include "winerror.h"
44 #include "drive.h"
45 #include "cdrom.h"
46 #include "file.h"
47 #include "heap.h"
48 #include "msdos.h"
49 #include "options.h"
50 #include "wine/port.h"
51 #include "task.h"
52 #include "debugtools.h"
54 DEFAULT_DEBUG_CHANNEL(dosfs)
55 DECLARE_DEBUG_CHANNEL(file)
57 typedef struct
59 char *root; /* root dir in Unix format without trailing / */
60 char *dos_cwd; /* cwd in DOS format without leading or trailing \ */
61 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
62 char *device; /* raw device path */
63 char label_conf[12]; /* drive label as cfg'd in wine.conf */
64 char label_read[12]; /* drive label as read from device */
65 DWORD serial_conf; /* drive serial number as cfg'd in wine.conf */
66 DRIVETYPE type; /* drive type */
67 UINT flags; /* drive flags */
68 dev_t dev; /* unix device number */
69 ino_t ino; /* unix inode number */
70 } DOSDRIVE;
73 static const char * const DRIVE_Types[] =
75 "floppy", /* TYPE_FLOPPY */
76 "hd", /* TYPE_HD */
77 "cdrom", /* TYPE_CDROM */
78 "network" /* TYPE_NETWORK */
82 /* Known filesystem types */
84 typedef struct
86 const char *name;
87 UINT flags;
88 } FS_DESCR;
90 static const FS_DESCR DRIVE_Filesystems[] =
92 { "unix", DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
93 { "msdos", DRIVE_SHORT_NAMES },
94 { "dos", DRIVE_SHORT_NAMES },
95 { "fat", DRIVE_SHORT_NAMES },
96 { "vfat", DRIVE_CASE_PRESERVING },
97 { "win95", DRIVE_CASE_PRESERVING },
98 { NULL, 0 }
102 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
103 static int DRIVE_CurDrive = -1;
105 static HTASK16 DRIVE_LastTask = 0;
108 /***********************************************************************
109 * DRIVE_GetDriveType
111 static DRIVETYPE DRIVE_GetDriveType( const char *name )
113 char buffer[20];
114 int i;
116 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
117 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
119 if (!strcasecmp( buffer, DRIVE_Types[i] )) return (DRIVETYPE)i;
121 MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
122 name, buffer );
123 return TYPE_HD;
127 /***********************************************************************
128 * DRIVE_GetFSFlags
130 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
132 const FS_DESCR *descr;
134 for (descr = DRIVE_Filesystems; descr->name; descr++)
135 if (!strcasecmp( value, descr->name )) return descr->flags;
136 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
137 name, value );
138 return DRIVE_CASE_PRESERVING;
142 /***********************************************************************
143 * DRIVE_Init
145 int DRIVE_Init(void)
147 int i, len, count = 0;
148 char name[] = "Drive A";
149 char path[MAX_PATHNAME_LEN];
150 char buffer[80];
151 struct stat drive_stat_buffer;
152 char *p;
153 DOSDRIVE *drive;
155 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
157 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
158 if (path[0])
160 p = path + strlen(path) - 1;
161 while ((p > path) && ((*p == '/') || (*p == '\\'))) *p-- = '\0';
162 if (!path[0]) strcpy( path, "/" );
164 if (stat( path, &drive_stat_buffer ))
166 MESSAGE("Could not stat %s, ignoring drive %c:\n", path, 'A' + i );
167 continue;
169 if (!S_ISDIR(drive_stat_buffer.st_mode))
171 MESSAGE("%s is not a directory, ignoring drive %c:\n",
172 path, 'A' + i );
173 continue;
176 drive->root = HEAP_strdupA( GetProcessHeap(), 0, path );
177 drive->dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
178 drive->unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
179 drive->type = DRIVE_GetDriveType( name );
180 drive->device = NULL;
181 drive->flags = 0;
182 drive->dev = drive_stat_buffer.st_dev;
183 drive->ino = drive_stat_buffer.st_ino;
185 /* Get the drive label */
186 PROFILE_GetWineIniString( name, "Label", name, drive->label_conf, 12 );
187 if ((len = strlen(drive->label_conf)) < 11)
189 /* Pad label with spaces */
190 memset( drive->label_conf + len, ' ', 11 - len );
191 drive->label_conf[11] = '\0';
194 /* Get the serial number */
195 PROFILE_GetWineIniString( name, "Serial", "12345678",
196 buffer, sizeof(buffer) );
197 drive->serial_conf = strtoul( buffer, NULL, 16 );
199 /* Get the filesystem type */
200 PROFILE_GetWineIniString( name, "Filesystem", "win95",
201 buffer, sizeof(buffer) );
202 drive->flags = DRIVE_GetFSFlags( name, buffer );
204 /* Get the device */
205 PROFILE_GetWineIniString( name, "Device", "",
206 buffer, sizeof(buffer) );
207 if (buffer[0])
209 drive->device = HEAP_strdupA( GetProcessHeap(), 0, buffer );
210 if (PROFILE_GetWineIniBool( name, "ReadVolInfo", 1))
211 drive->flags |= DRIVE_READ_VOL_INFO;
214 /* Get the FailReadOnly flag */
215 if (PROFILE_GetWineIniBool( name, "FailReadOnly", 0 ))
216 drive->flags |= DRIVE_FAIL_READ_ONLY;
218 /* Make the first hard disk the current drive */
219 if ((DRIVE_CurDrive == -1) && (drive->type == TYPE_HD))
220 DRIVE_CurDrive = i;
222 count++;
223 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
224 "flags=%08x dev=%x ino=%x\n",
225 name, path, DRIVE_Types[drive->type],
226 drive->label_conf, drive->serial_conf, drive->flags,
227 (int)drive->dev, (int)drive->ino );
229 else WARN("%s: not defined\n", name );
232 if (!count)
234 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
235 /* Create a C drive pointing to Unix root dir */
236 DOSDrives[2].root = HEAP_strdupA( GetProcessHeap(), 0, "/" );
237 DOSDrives[2].dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
238 DOSDrives[2].unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
239 strcpy( DOSDrives[2].label_conf, "Drive C " );
240 DOSDrives[2].serial_conf = 12345678;
241 DOSDrives[2].type = TYPE_HD;
242 DOSDrives[2].device = NULL;
243 DOSDrives[2].flags = 0;
244 DRIVE_CurDrive = 2;
247 /* Make sure the current drive is valid */
248 if (DRIVE_CurDrive == -1)
250 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
252 if (drive->root && !(drive->flags & DRIVE_DISABLED))
254 DRIVE_CurDrive = i;
255 break;
260 return 1;
264 /***********************************************************************
265 * DRIVE_IsValid
267 int DRIVE_IsValid( int drive )
269 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
270 return (DOSDrives[drive].root &&
271 !(DOSDrives[drive].flags & DRIVE_DISABLED));
275 /***********************************************************************
276 * DRIVE_GetCurrentDrive
278 int DRIVE_GetCurrentDrive(void)
280 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
281 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
282 return DRIVE_CurDrive;
286 /***********************************************************************
287 * DRIVE_SetCurrentDrive
289 int DRIVE_SetCurrentDrive( int drive )
291 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
292 if (!DRIVE_IsValid( drive ))
294 SetLastError( ERROR_INVALID_DRIVE );
295 return 0;
297 TRACE("%c:\n", 'A' + drive );
298 DRIVE_CurDrive = drive;
299 if (pTask) pTask->curdrive = drive | 0x80;
300 return 1;
304 /***********************************************************************
305 * DRIVE_FindDriveRoot
307 * Find a drive for which the root matches the beginning of the given path.
308 * This can be used to translate a Unix path into a drive + DOS path.
309 * Return value is the drive, or -1 on error. On success, path is modified
310 * to point to the beginning of the DOS path.
312 int DRIVE_FindDriveRoot( const char **path )
314 /* idea: check at all '/' positions.
315 * If the device and inode of that path is identical with the
316 * device and inode of the current drive then we found a solution.
317 * If there is another drive pointing to a deeper position in
318 * the file tree, we want to find that one, not the earlier solution.
320 int drive, rootdrive = -1;
321 char buffer[MAX_PATHNAME_LEN];
322 char *next = buffer;
323 const char *p = *path;
324 struct stat st;
326 strcpy( buffer, "/" );
327 for (;;)
329 if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
331 /* Find the drive */
333 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
335 if (!DOSDrives[drive].root ||
336 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
338 if ((DOSDrives[drive].dev == st.st_dev) &&
339 (DOSDrives[drive].ino == st.st_ino))
341 rootdrive = drive;
342 *path = p;
343 break;
347 /* Get the next path component */
349 *next++ = '/';
350 while ((*p == '/') || (*p == '\\')) p++;
351 if (!*p) break;
352 while (!IS_END_OF_NAME(*p)) *next++ = *p++;
353 *next = 0;
355 *next = 0;
357 if (rootdrive != -1)
358 TRACE("%s -> drive %c:, root='%s', name='%s'\n",
359 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, *path );
360 return rootdrive;
364 /***********************************************************************
365 * DRIVE_GetRoot
367 const char * DRIVE_GetRoot( int drive )
369 if (!DRIVE_IsValid( drive )) return NULL;
370 return DOSDrives[drive].root;
374 /***********************************************************************
375 * DRIVE_GetDosCwd
377 const char * DRIVE_GetDosCwd( int drive )
379 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
380 if (!DRIVE_IsValid( drive )) return NULL;
382 /* Check if we need to change the directory to the new task. */
383 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
384 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
385 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
387 /* Perform the task-switch */
388 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
389 DRIVE_LastTask = GetCurrentTask();
391 return DOSDrives[drive].dos_cwd;
395 /***********************************************************************
396 * DRIVE_GetUnixCwd
398 const char * DRIVE_GetUnixCwd( int drive )
400 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
401 if (!DRIVE_IsValid( drive )) return NULL;
403 /* Check if we need to change the directory to the new task. */
404 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
405 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
406 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
408 /* Perform the task-switch */
409 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
410 DRIVE_LastTask = GetCurrentTask();
412 return DOSDrives[drive].unix_cwd;
416 /***********************************************************************
417 * DRIVE_GetDevice
419 const char * DRIVE_GetDevice( int drive )
421 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
425 /***********************************************************************
426 * DRIVE_ReadSuperblock
428 * NOTE
429 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
430 * to check, that they are writing on a FAT filesystem !
432 int DRIVE_ReadSuperblock (int drive, char * buff)
434 #define DRIVE_SUPER 96
435 int fd;
436 off_t offs;
438 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
439 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
441 struct stat st;
442 if (!DOSDrives[drive].device)
443 ERR("No device configured for drive %c: !\n", 'A'+drive);
444 else
445 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
446 (stat(DOSDrives[drive].device, &st)) ?
447 "not available or symlink not valid ?" : "no permission");
448 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
449 PROFILE_UsageWineIni();
450 return -1;
453 switch(DOSDrives[drive].type)
455 case TYPE_FLOPPY:
456 case TYPE_HD:
457 offs = 0;
458 break;
459 case TYPE_CDROM:
460 offs = CDROM_Data_FindBestVoldesc(fd);
461 break;
462 default:
463 offs = 0;
464 break;
467 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs)) return -4;
468 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER) return -2;
470 switch(DOSDrives[drive].type)
472 case TYPE_FLOPPY:
473 case TYPE_HD:
474 if ((buff[0x26]!=0x29) || /* Check for FAT present */
475 /* FIXME: do really all FAT have their name beginning with
476 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
477 memcmp( buff+0x36,"FAT",3))
479 ERR("The filesystem is not FAT !! (device=%s)\n",
480 DOSDrives[drive].device);
481 return -3;
483 break;
484 case TYPE_CDROM:
485 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
486 return -3;
487 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
488 break;
489 default:
490 return -3;
491 break;
494 return close(fd);
498 /***********************************************************************
499 * DRIVE_WriteSuperblockEntry
501 * NOTE
502 * We are writing as little as possible (ie. not the whole SuperBlock)
503 * not to interfere with kernel. The drive can be mounted !
505 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
507 int fd;
509 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
511 ERR("Cannot open the device %s (for writing)\n",
512 DOSDrives[drive].device);
513 return -1;
515 if (lseek(fd,ofs,SEEK_SET)!=ofs)
517 ERR("lseek failed on device %s !\n",
518 DOSDrives[drive].device);
519 close(fd);
520 return -2;
522 if (write(fd,buff,len)!=len)
524 close(fd);
525 ERR("Cannot write on %s !\n",
526 DOSDrives[drive].device);
527 return -3;
529 return close (fd);
534 /***********************************************************************
535 * DRIVE_GetLabel
537 const char * DRIVE_GetLabel( int drive )
539 int read = 0;
540 char buff[DRIVE_SUPER];
541 int offs = -1;
543 if (!DRIVE_IsValid( drive )) return NULL;
544 if (DRIVE_GetType(drive) == TYPE_CDROM)
546 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
548 else
549 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
551 if (DRIVE_ReadSuperblock(drive,(char *) buff))
552 ERR("Invalid or unreadable superblock on %s (%c:).\n",
553 DOSDrives[drive].device, (char)(drive+'A'));
554 else {
555 if (DOSDrives[drive].type == TYPE_FLOPPY ||
556 DOSDrives[drive].type == TYPE_HD)
557 offs = 0x2b;
559 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
560 if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
561 DOSDrives[drive].label_read[11]='\0';
562 read = 1;
566 return (read) ?
567 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
571 /***********************************************************************
572 * DRIVE_GetSerialNumber
574 DWORD DRIVE_GetSerialNumber( int drive )
576 DWORD serial = 0;
577 char buff[DRIVE_SUPER];
579 if (!DRIVE_IsValid( drive )) return 0;
581 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
583 switch(DOSDrives[drive].type)
585 case TYPE_FLOPPY:
586 case TYPE_HD:
587 if (DRIVE_ReadSuperblock(drive,(char *) buff))
588 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
589 " Maybe not FAT?\n" ,
590 DOSDrives[drive].device, 'A'+drive);
591 else
592 serial = *((DWORD*)(buff+0x27));
593 break;
594 case TYPE_CDROM:
595 serial = CDROM_GetSerial(drive);
596 break;
597 default:
598 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
602 return (serial) ? serial : DOSDrives[drive].serial_conf;
606 /***********************************************************************
607 * DRIVE_SetSerialNumber
609 int DRIVE_SetSerialNumber( int drive, DWORD serial )
611 char buff[DRIVE_SUPER];
613 if (!DRIVE_IsValid( drive )) return 0;
615 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
617 if ((DOSDrives[drive].type != TYPE_FLOPPY) &&
618 (DOSDrives[drive].type != TYPE_HD)) return 0;
619 /* check, if the drive has a FAT filesystem */
620 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
621 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
622 return 1;
625 if (DOSDrives[drive].type == TYPE_CDROM) return 0;
626 DOSDrives[drive].serial_conf = serial;
627 return 1;
631 /***********************************************************************
632 * DRIVE_GetType
634 DRIVETYPE DRIVE_GetType( int drive )
636 if (!DRIVE_IsValid( drive )) return TYPE_INVALID;
637 return DOSDrives[drive].type;
641 /***********************************************************************
642 * DRIVE_GetFlags
644 UINT DRIVE_GetFlags( int drive )
646 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
647 return DOSDrives[drive].flags;
651 /***********************************************************************
652 * DRIVE_Chdir
654 int DRIVE_Chdir( int drive, const char *path )
656 DOS_FULL_NAME full_name;
657 char buffer[MAX_PATHNAME_LEN];
658 LPSTR unix_cwd;
659 BY_HANDLE_FILE_INFORMATION info;
660 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
662 strcpy( buffer, "A:" );
663 buffer[0] += drive;
664 TRACE("(%s,%s)\n", buffer, path );
665 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
667 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
668 if (!FILE_Stat( full_name.long_name, &info )) return 0;
669 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
671 SetLastError( ERROR_FILE_NOT_FOUND );
672 return 0;
674 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
675 while (*unix_cwd == '/') unix_cwd++;
677 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
678 'A' + drive, unix_cwd, full_name.short_name + 3 );
680 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
681 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
682 DOSDrives[drive].dos_cwd = HEAP_strdupA( GetProcessHeap(), 0,
683 full_name.short_name + 3 );
684 DOSDrives[drive].unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, unix_cwd );
686 if (pTask && (pTask->curdrive & 0x80) &&
687 ((pTask->curdrive & ~0x80) == drive))
689 lstrcpynA( pTask->curdir, full_name.short_name + 2,
690 sizeof(pTask->curdir) );
691 DRIVE_LastTask = GetCurrentTask();
693 return 1;
697 /***********************************************************************
698 * DRIVE_Disable
700 int DRIVE_Disable( int drive )
702 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
704 SetLastError( ERROR_INVALID_DRIVE );
705 return 0;
707 DOSDrives[drive].flags |= DRIVE_DISABLED;
708 return 1;
712 /***********************************************************************
713 * DRIVE_Enable
715 int DRIVE_Enable( int drive )
717 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
719 SetLastError( ERROR_INVALID_DRIVE );
720 return 0;
722 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
723 return 1;
727 /***********************************************************************
728 * DRIVE_SetLogicalMapping
730 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
732 /* If new_drive is already valid, do nothing and return 0
733 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
735 DOSDRIVE *old, *new;
737 old = DOSDrives + existing_drive;
738 new = DOSDrives + new_drive;
740 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
741 !old->root ||
742 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
744 SetLastError( ERROR_INVALID_DRIVE );
745 return 0;
748 if ( new->root )
750 TRACE("Can't map drive %c: to already existing drive %c:\n",
751 'A' + existing_drive, 'A' + new_drive );
752 /* it is already mapped there, so return success */
753 if (!strcmp(old->root,new->root))
754 return 1;
755 return 0;
758 new->root = HEAP_strdupA( GetProcessHeap(), 0, old->root );
759 new->dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->dos_cwd );
760 new->unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->unix_cwd );
761 new->device = HEAP_strdupA( GetProcessHeap(), 0, old->device );
762 memcpy ( new->label_conf, old->label_conf, 12 );
763 memcpy ( new->label_read, old->label_read, 12 );
764 new->serial_conf = old->serial_conf;
765 new->type = old->type;
766 new->flags = old->flags;
767 new->dev = old->dev;
768 new->ino = old->ino;
770 TRACE("Drive %c: is now equal to drive %c:\n",
771 'A' + new_drive, 'A' + existing_drive );
773 return 1;
777 /***********************************************************************
778 * DRIVE_OpenDevice
780 * Open the drive raw device and return a Unix fd (or -1 on error).
782 int DRIVE_OpenDevice( int drive, int flags )
784 if (!DRIVE_IsValid( drive )) return -1;
785 return open( DOSDrives[drive].device, flags );
789 /***********************************************************************
790 * DRIVE_RawRead
792 * Read raw sectors from a device
794 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
796 int fd;
798 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
800 lseek( fd, begin * 512, SEEK_SET );
801 /* FIXME: check errors */
802 read( fd, dataptr, nr_sect * 512 );
803 close( fd );
805 else
807 memset(dataptr, 0, nr_sect * 512);
808 if (fake_success)
810 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
811 if (begin == 1) *dataptr = 0xf8;
813 else
814 return 0;
816 return 1;
820 /***********************************************************************
821 * DRIVE_RawWrite
823 * Write raw sectors to a device
825 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
827 int fd;
829 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
831 lseek( fd, begin * 512, SEEK_SET );
832 /* FIXME: check errors */
833 write( fd, dataptr, nr_sect * 512 );
834 close( fd );
836 else
837 if (!(fake_success))
838 return 0;
840 return 1;
844 /***********************************************************************
845 * DRIVE_GetFreeSpace
847 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
848 PULARGE_INTEGER available )
850 struct statfs info;
851 unsigned long long bigsize,bigavail=0;
853 if (!DRIVE_IsValid(drive))
855 SetLastError( ERROR_INVALID_DRIVE );
856 return 0;
859 /* FIXME: add autoconf check for this */
860 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
861 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
862 #else
863 if (statfs( DOSDrives[drive].root, &info) < 0)
864 #endif
866 FILE_SetDosError();
867 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
868 return 0;
871 bigsize = (unsigned long long)info.f_bsize
872 * (unsigned long long)info.f_blocks;
873 #ifdef STATFS_HAS_BAVAIL
874 bigavail = (unsigned long long)info.f_bavail
875 * (unsigned long long)info.f_bsize;
876 #else
877 # ifdef STATFS_HAS_BFREE
878 bigavail = (unsigned long long)info.f_bfree
879 * (unsigned long long)info.f_bsize;
880 # else
881 # error "statfs has no bfree/bavail member!"
882 # endif
883 #endif
884 size->s.LowPart = (DWORD)bigsize;
885 size->s.HighPart = (DWORD)(bigsize>>32);
886 if (DRIVE_GetType(drive) == TYPE_CDROM)
887 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
888 available->s.LowPart = 0;
889 available->s.HighPart = 0;
891 else
893 available->s.LowPart = (DWORD)bigavail;
894 available->s.HighPart = (DWORD)(bigavail>>32);
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;
921 /***********************************************************************
922 * GetDiskFreeSpace16 (KERNEL.422)
924 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
925 LPDWORD sector_bytes, LPDWORD free_clusters,
926 LPDWORD total_clusters )
928 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
929 free_clusters, total_clusters );
933 /***********************************************************************
934 * GetDiskFreeSpaceA (KERNEL32.206)
936 * Fails if expression resulting from current drive's dir and "root"
937 * is not a root dir of the target drive.
939 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
940 * if the corresponding info is unneeded.
942 * FIXME: needs to support UNC names from Win95 OSR2 on.
944 * Behaviour under Win95a:
945 * CurrDir root result
946 * "E:\\TEST" "E:" FALSE
947 * "E:\\" "E:" TRUE
948 * "E:\\" "E" FALSE
949 * "E:\\" "\\" TRUE
950 * "E:\\TEST" "\\" TRUE
951 * "E:\\TEST" ":\\" FALSE
952 * "E:\\TEST" "E:\\" TRUE
953 * "E:\\TEST" "" FALSE
954 * "E:\\" "" FALSE (!)
955 * "E:\\" 0x0 TRUE
956 * "E:\\TEST" 0x0 TRUE (!)
957 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
958 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
960 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
961 LPDWORD sector_bytes, LPDWORD free_clusters,
962 LPDWORD total_clusters )
964 int drive, sec_size;
965 ULARGE_INTEGER size,available;
966 LPCSTR path;
967 DWORD cluster_sec;
969 if ((!root) || (strcmp(root,"\\") == 0))
970 drive = DRIVE_GetCurrentDrive();
971 else
972 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
974 drive = toupper(root[0]) - 'A';
975 path = &root[2];
976 if (path[0] == '\0')
977 path = DRIVE_GetDosCwd(drive);
978 else
979 if (path[0] == '\\')
980 path++;
981 if (strlen(path)) /* oops, we are in a subdir */
982 return FALSE;
984 else
985 return FALSE;
987 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
989 /* Cap the size and available at 2GB as per specs. */
990 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
992 size.s.HighPart = 0;
993 size.s.LowPart = 0x7fffffff;
995 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
997 available.s.HighPart =0;
998 available.s.LowPart = 0x7fffffff;
1000 sec_size = (DRIVE_GetType(drive)==TYPE_CDROM) ? 2048 : 512;
1001 size.s.LowPart /= sec_size;
1002 available.s.LowPart /= sec_size;
1003 /* fixme: probably have to adjust those variables too for CDFS */
1004 cluster_sec = 1;
1005 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1007 if (cluster_sectors)
1008 *cluster_sectors = cluster_sec;
1009 if (sector_bytes)
1010 *sector_bytes = sec_size;
1011 if (free_clusters)
1012 *free_clusters = available.s.LowPart / cluster_sec;
1013 if (total_clusters)
1014 *total_clusters = size.s.LowPart / cluster_sec;
1015 return TRUE;
1019 /***********************************************************************
1020 * GetDiskFreeSpaceW (KERNEL32.207)
1022 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1023 LPDWORD sector_bytes, LPDWORD free_clusters,
1024 LPDWORD total_clusters )
1026 LPSTR xroot;
1027 BOOL ret;
1029 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1030 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
1031 free_clusters, total_clusters );
1032 HeapFree( GetProcessHeap(), 0, xroot );
1033 return ret;
1037 /***********************************************************************
1038 * GetDiskFreeSpaceExA (KERNEL32.871)
1040 * This function is used to aquire the size of the available and
1041 * total space on a logical volume.
1043 * RETURNS
1045 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1046 * detailed error information.
1049 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1050 PULARGE_INTEGER avail,
1051 PULARGE_INTEGER total,
1052 PULARGE_INTEGER totalfree)
1054 int drive;
1055 ULARGE_INTEGER size,available;
1057 if (!root) drive = DRIVE_GetCurrentDrive();
1058 else
1060 if ((root[1]) && ((root[1] != ':') || (root[2] != '\\')))
1062 FIXME("there are valid root names which are not supported yet\n");
1063 /* ..like UNC names, for instance. */
1065 WARN("invalid root '%s'\n", root );
1066 return FALSE;
1068 drive = toupper(root[0]) - 'A';
1071 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1073 if (total)
1075 total->s.HighPart = size.s.HighPart;
1076 total->s.LowPart = size.s.LowPart;
1079 if (totalfree)
1081 totalfree->s.HighPart = available.s.HighPart;
1082 totalfree->s.LowPart = available.s.LowPart;
1085 if (avail)
1087 if (FIXME_ON(dosfs))
1089 /* On Windows2000, we need to check the disk quota
1090 allocated for the user owning the calling process. We
1091 don't want to be more obtrusive than necessary with the
1092 FIXME messages, so don't print the FIXME unless Wine is
1093 actually masquerading as Windows2000. */
1095 OSVERSIONINFOA ovi;
1096 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1097 if (GetVersionExA(&ovi))
1099 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1100 FIXME("no per-user quota support yet\n");
1104 /* Quick hack, should eventually be fixed to work 100% with
1105 Windows2000 (see comment above). */
1106 avail->s.HighPart = available.s.HighPart;
1107 avail->s.LowPart = available.s.LowPart;
1110 return TRUE;
1113 /***********************************************************************
1114 * GetDiskFreeSpaceExW (KERNEL32.873)
1116 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1117 PULARGE_INTEGER total,
1118 PULARGE_INTEGER totalfree)
1120 LPSTR xroot;
1121 BOOL ret;
1123 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1124 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1125 HeapFree( GetProcessHeap(), 0, xroot );
1126 return ret;
1129 /***********************************************************************
1130 * GetDriveType16 (KERNEL.136)
1131 * This function returns the type of a drive in Win16.
1132 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1133 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1134 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1135 * do any pseudo-clever changes.
1137 * RETURNS
1138 * drivetype DRIVE_xxx
1140 UINT16 WINAPI GetDriveType16(
1141 UINT16 drive /* [in] number (NOT letter) of drive */
1143 TRACE("(%c:)\n", 'A' + drive );
1144 switch(DRIVE_GetType(drive))
1146 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
1147 case TYPE_HD: return DRIVE_FIXED;
1148 case TYPE_CDROM: return DRIVE_REMOTE;
1149 case TYPE_NETWORK: return DRIVE_REMOTE;
1150 case TYPE_INVALID:
1151 default: return DRIVE_CANNOTDETERMINE;
1156 /***********************************************************************
1157 * GetDriveTypeA (KERNEL32.208)
1159 * Returns the type of the disk drive specified. If root is NULL the
1160 * root of the current directory is used.
1162 * RETURNS
1164 * Type of drive (from Win32 SDK):
1166 * DRIVE_UNKNOWN unable to find out anything about the drive
1167 * DRIVE_NO_ROOT_DIR nonexistent root dir
1168 * DRIVE_REMOVABLE the disk can be removed from the machine
1169 * DRIVE_FIXED the disk can not be removed from the machine
1170 * DRIVE_REMOTE network disk
1171 * DRIVE_CDROM CDROM drive
1172 * DRIVE_RAMDISK virtual disk in RAM
1174 * DRIVE_DOESNOTEXIST FIXME Not valid return value
1175 * DRIVE_CANNOTDETERMINE FIXME Not valid return value
1177 * BUGS
1179 * Currently returns DRIVE_DOESNOTEXIST and DRIVE_CANNOTDETERMINE
1180 * when it really should return DRIVE_NO_ROOT_DIR and DRIVE_UNKNOWN.
1181 * Why were the former defines used?
1183 * DRIVE_RAMDISK is unsupported.
1185 UINT WINAPI GetDriveTypeA(LPCSTR root /* String describing drive */)
1187 int drive;
1188 TRACE("(%s)\n", debugstr_a(root));
1190 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1191 else
1193 if ((root[1]) && (root[1] != ':'))
1195 WARN("invalid root '%s'\n", debugstr_a(root));
1196 return DRIVE_DOESNOTEXIST;
1198 drive = toupper(root[0]) - 'A';
1200 switch(DRIVE_GetType(drive))
1202 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
1203 case TYPE_HD: return DRIVE_FIXED;
1204 case TYPE_CDROM: return DRIVE_CDROM;
1205 case TYPE_NETWORK: return DRIVE_REMOTE;
1206 case TYPE_INVALID: return DRIVE_DOESNOTEXIST;
1207 default: return DRIVE_CANNOTDETERMINE;
1212 /***********************************************************************
1213 * GetDriveTypeW (KERNEL32.209)
1215 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1217 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1218 UINT ret = GetDriveTypeA( xpath );
1219 HeapFree( GetProcessHeap(), 0, xpath );
1220 return ret;
1224 /***********************************************************************
1225 * GetCurrentDirectory16 (KERNEL.411)
1227 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1229 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1233 /***********************************************************************
1234 * GetCurrentDirectoryA (KERNEL32.196)
1236 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1238 UINT ret;
1239 char longname[MAX_PATHNAME_LEN];
1240 char shortname[MAX_PATHNAME_LEN];
1241 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1242 if ( ret > MAX_PATHNAME_LEN ) {
1243 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1244 return ret;
1246 GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1247 ret = strlen( longname ) + 1;
1248 if (ret > buflen) return ret;
1249 strcpy(buf, longname);
1250 return ret - 1;
1253 /***********************************************************************
1254 * GetCurrentDirectoryW (KERNEL32.197)
1256 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1258 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1259 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1260 if (ret < buflen) lstrcpyAtoW ( buf, xpath );
1261 HeapFree( GetProcessHeap(), 0, xpath );
1262 return ret;
1266 /***********************************************************************
1267 * SetCurrentDirectory (KERNEL.412)
1269 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1271 return SetCurrentDirectoryA( dir );
1275 /***********************************************************************
1276 * SetCurrentDirectoryA (KERNEL32.479)
1278 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1280 int drive, olddrive = DRIVE_GetCurrentDrive();
1282 if (!dir) {
1283 ERR_(file)("(NULL)!\n");
1284 return FALSE;
1286 if (dir[0] && (dir[1]==':'))
1288 drive = toupper( *dir ) - 'A';
1289 dir += 2;
1291 else
1292 drive = olddrive;
1294 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1295 sets pTask->curdir only if pTask->curdrive is drive */
1296 if (!(DRIVE_SetCurrentDrive( drive )))
1297 return FALSE;
1298 /* FIXME: what about empty strings? Add a \\ ? */
1299 if (!DRIVE_Chdir( drive, dir )) {
1300 DRIVE_SetCurrentDrive(olddrive);
1301 return FALSE;
1303 return TRUE;
1307 /***********************************************************************
1308 * SetCurrentDirectoryW (KERNEL32.480)
1310 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1312 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1313 BOOL res = SetCurrentDirectoryA( dir );
1314 HeapFree( GetProcessHeap(), 0, dir );
1315 return res;
1319 /***********************************************************************
1320 * GetLogicalDriveStringsA (KERNEL32.231)
1322 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1324 int drive, count;
1326 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1327 if (DRIVE_IsValid(drive)) count++;
1328 if ((count * 4) + 1 <= len)
1330 LPSTR p = buffer;
1331 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1332 if (DRIVE_IsValid(drive))
1334 *p++ = 'a' + drive;
1335 *p++ = ':';
1336 *p++ = '\\';
1337 *p++ = '\0';
1339 *p = '\0';
1340 return count * 4;
1342 else
1343 return (count * 4) + 1; /* account for terminating null */
1344 /* The API tells about these different return values */
1348 /***********************************************************************
1349 * GetLogicalDriveStringsW (KERNEL32.232)
1351 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1353 int drive, count;
1355 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1356 if (DRIVE_IsValid(drive)) count++;
1357 if (count * 4 * sizeof(WCHAR) <= len)
1359 LPWSTR p = buffer;
1360 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1361 if (DRIVE_IsValid(drive))
1363 *p++ = (WCHAR)('a' + drive);
1364 *p++ = (WCHAR)':';
1365 *p++ = (WCHAR)'\\';
1366 *p++ = (WCHAR)'\0';
1368 *p = (WCHAR)'\0';
1370 return count * 4 * sizeof(WCHAR);
1374 /***********************************************************************
1375 * GetLogicalDrives (KERNEL32.233)
1377 DWORD WINAPI GetLogicalDrives(void)
1379 DWORD ret = 0;
1380 int drive;
1382 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1384 if ( (DRIVE_IsValid(drive)) ||
1385 (DOSDrives[drive].type == TYPE_CDROM)) /* audio CD is also valid */
1386 ret |= (1 << drive);
1388 return ret;
1392 /***********************************************************************
1393 * GetVolumeInformationA (KERNEL32.309)
1395 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1396 DWORD label_len, DWORD *serial,
1397 DWORD *filename_len, DWORD *flags,
1398 LPSTR fsname, DWORD fsname_len )
1400 int drive;
1401 char *cp;
1403 /* FIXME, SetLastError()s missing */
1405 if (!root) drive = DRIVE_GetCurrentDrive();
1406 else
1408 if ((root[1]) && (root[1] != ':'))
1410 WARN("invalid root '%s'\n",root);
1411 return FALSE;
1413 drive = toupper(root[0]) - 'A';
1415 if (!DRIVE_IsValid( drive )) return FALSE;
1416 if (label)
1418 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1419 for (cp = label; *cp; cp++);
1420 while (cp != label && *(cp-1) == ' ') cp--;
1421 *cp = '\0';
1423 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1425 /* Set the filesystem information */
1426 /* Note: we only emulate a FAT fs at present */
1428 if (filename_len) {
1429 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1430 *filename_len = 12;
1431 else
1432 *filename_len = 255;
1434 if (flags)
1436 *flags=0;
1437 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1438 *flags|=FS_CASE_SENSITIVE;
1439 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1440 *flags|=FS_CASE_IS_PRESERVED;
1442 if (fsname) {
1443 /* Diablo checks that return code ... */
1444 if (DRIVE_GetType(drive)==TYPE_CDROM)
1445 lstrcpynA( fsname, "CDFS", fsname_len );
1446 else
1447 lstrcpynA( fsname, "FAT", fsname_len );
1449 return TRUE;
1453 /***********************************************************************
1454 * GetVolumeInformationW (KERNEL32.310)
1456 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1457 DWORD label_len, DWORD *serial,
1458 DWORD *filename_len, DWORD *flags,
1459 LPWSTR fsname, DWORD fsname_len )
1461 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1462 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1463 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1464 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1465 filename_len, flags, xfsname,
1466 fsname_len );
1467 if (ret)
1469 if (label) lstrcpyAtoW( label, xvolname );
1470 if (fsname) lstrcpyAtoW( fsname, xfsname );
1472 HeapFree( GetProcessHeap(), 0, xroot );
1473 HeapFree( GetProcessHeap(), 0, xvolname );
1474 HeapFree( GetProcessHeap(), 0, xfsname );
1475 return ret;
1478 /***********************************************************************
1479 * SetVolumeLabelA (KERNEL32.675)
1481 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1483 int drive;
1485 /* FIXME, SetLastErrors missing */
1487 if (!root) drive = DRIVE_GetCurrentDrive();
1488 else
1490 if ((root[1]) && (root[1] != ':'))
1492 WARN("invalid root '%s'\n",root);
1493 return FALSE;
1495 drive = toupper(root[0]) - 'A';
1497 if (!DRIVE_IsValid( drive )) return FALSE;
1499 /* some copy protection stuff check this */
1500 if (DRIVE_GetType( drive ) == TYPE_CDROM) return FALSE;
1502 FIXME("(%s,%s),stub!\n", root, volname);
1503 return TRUE;
1506 /***********************************************************************
1507 * SetVolumeLabelW (KERNEL32.676)
1509 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1511 LPSTR xroot, xvol;
1512 BOOL ret;
1514 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1515 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1516 ret = SetVolumeLabelA( xroot, xvol );
1517 HeapFree( GetProcessHeap(), 0, xroot );
1518 HeapFree( GetProcessHeap(), 0, xvol );
1519 return ret;