Moved SystemHeap allocations to the process heap.
[wine.git] / files / drive.c
blobdfe2b876ae29e846aef9e6dc4e9a8c45c2be53d5
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 BOOL read_volinfo; /* read the volume info from the device ? */
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 path[MAX_PATHNAME_LEN];
151 char buffer[80];
152 struct stat drive_stat_buffer;
153 char *p;
154 DOSDRIVE *drive;
156 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
158 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
159 if (path[0])
161 p = path + strlen(path) - 1;
162 while ((p > path) && ((*p == '/') || (*p == '\\'))) *p-- = '\0';
163 if (!path[0]) strcpy( path, "/" );
165 if (stat( path, &drive_stat_buffer ))
167 MESSAGE("Could not stat %s, ignoring drive %c:\n", path, 'A' + i );
168 continue;
170 if (!S_ISDIR(drive_stat_buffer.st_mode))
172 MESSAGE("%s is not a directory, ignoring drive %c:\n",
173 path, 'A' + i );
174 continue;
177 drive->root = HEAP_strdupA( GetProcessHeap(), 0, path );
178 drive->dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
179 drive->unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
180 drive->type = DRIVE_GetDriveType( name );
181 drive->device = NULL;
182 drive->flags = 0;
183 drive->dev = drive_stat_buffer.st_dev;
184 drive->ino = drive_stat_buffer.st_ino;
186 /* Get the drive label */
187 PROFILE_GetWineIniString( name, "Label", name, drive->label_conf, 12 );
188 if ((len = strlen(drive->label_conf)) < 11)
190 /* Pad label with spaces */
191 memset( drive->label_conf + len, ' ', 11 - len );
192 drive->label_conf[11] = '\0';
195 /* Get the serial number */
196 PROFILE_GetWineIniString( name, "Serial", "12345678",
197 buffer, sizeof(buffer) );
198 drive->serial_conf = strtoul( buffer, NULL, 16 );
200 /* Get the filesystem type */
201 PROFILE_GetWineIniString( name, "Filesystem", "win95",
202 buffer, sizeof(buffer) );
203 drive->flags = DRIVE_GetFSFlags( name, buffer );
205 /* Get the device */
206 PROFILE_GetWineIniString( name, "Device", "",
207 buffer, sizeof(buffer) );
208 if (buffer[0])
210 drive->device = HEAP_strdupA( GetProcessHeap(), 0, buffer );
211 drive->read_volinfo =
212 (BOOL)PROFILE_GetWineIniInt( name, "ReadVolInfo", 1);
214 else
215 drive->read_volinfo = FALSE;
217 /* Make the first hard disk the current drive */
218 if ((DRIVE_CurDrive == -1) && (drive->type == TYPE_HD))
219 DRIVE_CurDrive = i;
221 count++;
222 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
223 "flags=%08x dev=%x ino=%x\n",
224 name, path, DRIVE_Types[drive->type],
225 drive->label_conf, drive->serial_conf, drive->flags,
226 (int)drive->dev, (int)drive->ino );
228 else WARN("%s: not defined\n", name );
231 if (!count)
233 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
234 /* Create a C drive pointing to Unix root dir */
235 DOSDrives[2].root = HEAP_strdupA( GetProcessHeap(), 0, "/" );
236 DOSDrives[2].dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
237 DOSDrives[2].unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
238 strcpy( DOSDrives[2].label_conf, "Drive C " );
239 DOSDrives[2].serial_conf = 12345678;
240 DOSDrives[2].type = TYPE_HD;
241 DOSDrives[2].flags = 0;
242 DRIVE_CurDrive = 2;
245 /* Make sure the current drive is valid */
246 if (DRIVE_CurDrive == -1)
248 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
250 if (drive->root && !(drive->flags & DRIVE_DISABLED))
252 DRIVE_CurDrive = i;
253 break;
258 return 1;
262 /***********************************************************************
263 * DRIVE_IsValid
265 int DRIVE_IsValid( int drive )
267 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
268 return (DOSDrives[drive].root &&
269 !(DOSDrives[drive].flags & DRIVE_DISABLED));
273 /***********************************************************************
274 * DRIVE_GetCurrentDrive
276 int DRIVE_GetCurrentDrive(void)
278 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
279 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
280 return DRIVE_CurDrive;
284 /***********************************************************************
285 * DRIVE_SetCurrentDrive
287 int DRIVE_SetCurrentDrive( int drive )
289 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
290 if (!DRIVE_IsValid( drive ))
292 SetLastError( ERROR_INVALID_DRIVE );
293 return 0;
295 TRACE("%c:\n", 'A' + drive );
296 DRIVE_CurDrive = drive;
297 if (pTask) pTask->curdrive = drive | 0x80;
298 return 1;
302 /***********************************************************************
303 * DRIVE_FindDriveRoot
305 * Find a drive for which the root matches the beginning of the given path.
306 * This can be used to translate a Unix path into a drive + DOS path.
307 * Return value is the drive, or -1 on error. On success, path is modified
308 * to point to the beginning of the DOS path.
310 int DRIVE_FindDriveRoot( const char **path )
312 /* idea: check at all '/' positions.
313 * If the device and inode of that path is identical with the
314 * device and inode of the current drive then we found a solution.
315 * If there is another drive pointing to a deeper position in
316 * the file tree, we want to find that one, not the earlier solution.
318 int drive, rootdrive = -1;
319 char buffer[MAX_PATHNAME_LEN];
320 char *next = buffer;
321 const char *p = *path;
322 struct stat st;
324 strcpy( buffer, "/" );
325 for (;;)
327 if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
329 /* Find the drive */
331 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
333 if (!DOSDrives[drive].root ||
334 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
336 if ((DOSDrives[drive].dev == st.st_dev) &&
337 (DOSDrives[drive].ino == st.st_ino))
339 rootdrive = drive;
340 *path = p;
344 /* Get the next path component */
346 *next++ = '/';
347 while ((*p == '/') || (*p == '\\')) p++;
348 if (!*p) break;
349 while (!IS_END_OF_NAME(*p)) *next++ = *p++;
350 *next = 0;
352 *next = 0;
354 if (rootdrive != -1)
355 TRACE("%s -> drive %c:, root='%s', name='%s'\n",
356 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, *path );
357 return rootdrive;
361 /***********************************************************************
362 * DRIVE_GetRoot
364 const char * DRIVE_GetRoot( int drive )
366 if (!DRIVE_IsValid( drive )) return NULL;
367 return DOSDrives[drive].root;
371 /***********************************************************************
372 * DRIVE_GetDosCwd
374 const char * DRIVE_GetDosCwd( int drive )
376 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
377 if (!DRIVE_IsValid( drive )) return NULL;
379 /* Check if we need to change the directory to the new task. */
380 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
381 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
382 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
384 /* Perform the task-switch */
385 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
386 DRIVE_LastTask = GetCurrentTask();
388 return DOSDrives[drive].dos_cwd;
392 /***********************************************************************
393 * DRIVE_GetUnixCwd
395 const char * DRIVE_GetUnixCwd( int drive )
397 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
398 if (!DRIVE_IsValid( drive )) return NULL;
400 /* Check if we need to change the directory to the new task. */
401 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
402 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
403 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
405 /* Perform the task-switch */
406 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
407 DRIVE_LastTask = GetCurrentTask();
409 return DOSDrives[drive].unix_cwd;
413 /***********************************************************************
414 * DRIVE_GetDevice
416 const char * DRIVE_GetDevice( int drive )
418 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
422 /***********************************************************************
423 * DRIVE_ReadSuperblock
425 * Used in DRIVE_GetLabel
427 int DRIVE_ReadSuperblock (int drive, char * buff)
429 #define DRIVE_SUPER 96
430 int fd;
431 off_t offs;
433 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
434 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
436 struct stat st;
437 if (!DOSDrives[drive].device)
438 ERR("No device configured for drive %c: !\n", 'A'+drive);
439 else
440 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
441 (stat(DOSDrives[drive].device, &st)) ?
442 "not available or symlink not valid ?" : "no permission");
443 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
444 PROFILE_UsageWineIni();
445 return -1;
448 switch(DOSDrives[drive].type)
450 case TYPE_FLOPPY:
451 case TYPE_HD:
452 offs = 0;
453 break;
454 case TYPE_CDROM:
455 /* FIXME: Maybe we should search for the first data track on the CD,
456 not just assume that it is the first track */
457 offs = (off_t)2048*(16+0);
458 break;
459 default:
460 offs = 0;
461 break;
464 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs)) return -4;
465 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER) return -2;
467 switch(DOSDrives[drive].type)
469 case TYPE_FLOPPY:
470 case TYPE_HD:
471 if (buff[0x26]!=0x29) /* Check for FAT present */
472 return -3;
473 break;
474 case TYPE_CDROM:
475 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
476 return -3;
477 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
478 break;
479 default:
480 return -3;
481 break;
484 return close(fd);
488 /***********************************************************************
489 * DRIVE_GetLabel
491 const char * DRIVE_GetLabel( int drive )
493 int read = 0;
494 char buff[DRIVE_SUPER];
495 int offs = -1;
497 if (!DRIVE_IsValid( drive )) return NULL;
498 if (DRIVE_GetType(drive) == TYPE_CDROM)
500 WINE_CDAUDIO wcda;
502 if (!(CDROM_Open(&wcda, drive)))
504 int media = CDROM_GetMediaType(&wcda);
506 if (media == CDS_AUDIO)
508 strcpy(DOSDrives[drive].label_read, "Audio CD ");
509 read = 1;
511 else
512 if (media == CDS_NO_INFO)
514 strcpy(DOSDrives[drive].label_read, " ");
515 read = 1;
518 CDROM_Close(&wcda);
521 if ((!read) && (DOSDrives[drive].read_volinfo))
523 if (DRIVE_ReadSuperblock(drive,(char *) buff))
524 ERR("Invalid or unreadable superblock on %s (%c:).\n",
525 DOSDrives[drive].device, (char)(drive+'A'));
526 else {
527 if (DOSDrives[drive].type == TYPE_CDROM)
528 offs = 40;
529 else
530 if (DOSDrives[drive].type == TYPE_FLOPPY ||
531 DOSDrives[drive].type == TYPE_HD)
532 offs = 0x2b;
534 /* FIXME: ISO9660 uses 32-bytes long label. Should we do also? */
535 if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
536 DOSDrives[drive].label_read[11]='\0';
537 read = 1;
541 return (read) ?
542 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
546 /***********************************************************************
547 * DRIVE_GetSerialNumber
549 DWORD DRIVE_GetSerialNumber( int drive )
551 DWORD serial = 0;
552 char buff[DRIVE_SUPER];
554 if (!DRIVE_IsValid( drive )) return 0;
556 if (DOSDrives[drive].read_volinfo)
558 switch(DOSDrives[drive].type)
560 case TYPE_FLOPPY:
561 case TYPE_HD:
562 if (DRIVE_ReadSuperblock(drive,(char *) buff))
563 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
564 " Maybe not FAT?\n" ,
565 DOSDrives[drive].device, 'A'+drive);
566 else
567 serial = *((DWORD*)(buff+0x27));
568 break;
569 case TYPE_CDROM:
570 serial = CDROM_GetSerial(drive);
571 break;
572 default:
573 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
577 return (serial) ? serial : DOSDrives[drive].serial_conf;
581 /***********************************************************************
582 * DRIVE_SetSerialNumber
584 int DRIVE_SetSerialNumber( int drive, DWORD serial )
586 if (!DRIVE_IsValid( drive )) return 0;
587 if ((DOSDrives[drive].read_volinfo) &&
588 (DOSDrives[drive].type != TYPE_CDROM))
589 FIXME("Setting the serial number is useless for drive %c: until writing it is properly implemented, as this drive reads it from the device.\n", 'A'+drive);
590 DOSDrives[drive].serial_conf = serial;
591 return 1;
595 /***********************************************************************
596 * DRIVE_GetType
598 DRIVETYPE DRIVE_GetType( int drive )
600 if (!DRIVE_IsValid( drive )) return TYPE_INVALID;
601 return DOSDrives[drive].type;
605 /***********************************************************************
606 * DRIVE_GetFlags
608 UINT DRIVE_GetFlags( int drive )
610 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
611 return DOSDrives[drive].flags;
615 /***********************************************************************
616 * DRIVE_Chdir
618 int DRIVE_Chdir( int drive, const char *path )
620 DOS_FULL_NAME full_name;
621 char buffer[MAX_PATHNAME_LEN];
622 LPSTR unix_cwd;
623 BY_HANDLE_FILE_INFORMATION info;
624 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
626 strcpy( buffer, "A:" );
627 buffer[0] += drive;
628 TRACE("(%c:,%s)\n", buffer[0], path );
629 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
631 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
632 if (!FILE_Stat( full_name.long_name, &info )) return 0;
633 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
635 SetLastError( ERROR_FILE_NOT_FOUND );
636 return 0;
638 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
639 while (*unix_cwd == '/') unix_cwd++;
641 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
642 'A' + drive, unix_cwd, full_name.short_name + 3 );
644 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
645 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
646 DOSDrives[drive].dos_cwd = HEAP_strdupA( GetProcessHeap(), 0,
647 full_name.short_name + 3 );
648 DOSDrives[drive].unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, unix_cwd );
650 if (pTask && (pTask->curdrive & 0x80) &&
651 ((pTask->curdrive & ~0x80) == drive))
653 lstrcpynA( pTask->curdir, full_name.short_name + 2,
654 sizeof(pTask->curdir) );
655 DRIVE_LastTask = GetCurrentTask();
657 return 1;
661 /***********************************************************************
662 * DRIVE_Disable
664 int DRIVE_Disable( int drive )
666 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
668 SetLastError( ERROR_INVALID_DRIVE );
669 return 0;
671 DOSDrives[drive].flags |= DRIVE_DISABLED;
672 return 1;
676 /***********************************************************************
677 * DRIVE_Enable
679 int DRIVE_Enable( int drive )
681 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
683 SetLastError( ERROR_INVALID_DRIVE );
684 return 0;
686 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
687 return 1;
691 /***********************************************************************
692 * DRIVE_SetLogicalMapping
694 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
696 /* If new_drive is already valid, do nothing and return 0
697 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
699 DOSDRIVE *old, *new;
701 old = DOSDrives + existing_drive;
702 new = DOSDrives + new_drive;
704 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
705 !old->root ||
706 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
708 SetLastError( ERROR_INVALID_DRIVE );
709 return 0;
712 if ( new->root )
714 TRACE("Can\'t map drive %c to drive %c - drive %c already exists\n",
715 'A' + existing_drive, 'A' + new_drive, 'A' + new_drive );
716 /* it is already mapped there, so return success */
717 if (!strcmp(old->root,new->root))
718 return 1;
719 return 0;
722 new->root = HEAP_strdupA( GetProcessHeap(), 0, old->root );
723 new->dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->dos_cwd );
724 new->unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->unix_cwd );
725 memcpy ( new->label_conf, old->label_conf, 12 );
726 new->serial_conf = old->serial_conf;
727 new->type = old->type;
728 new->flags = old->flags;
729 new->dev = old->dev;
730 new->ino = old->ino;
732 TRACE("Drive %c is now equal to drive %c\n",
733 'A' + new_drive, 'A' + existing_drive );
735 return 1;
739 /***********************************************************************
740 * DRIVE_OpenDevice
742 * Open the drive raw device and return a Unix fd (or -1 on error).
744 int DRIVE_OpenDevice( int drive, int flags )
746 if (!DRIVE_IsValid( drive )) return -1;
747 return open( DOSDrives[drive].device, flags );
751 /***********************************************************************
752 * DRIVE_RawRead
754 * Read raw sectors from a device
756 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
758 int fd;
760 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
762 lseek( fd, begin * 512, SEEK_SET );
763 /* FIXME: check errors */
764 read( fd, dataptr, nr_sect * 512 );
765 close( fd );
767 else
769 memset(dataptr, 0, nr_sect * 512);
770 if (fake_success)
772 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
773 if (begin == 1) *dataptr = 0xf8;
775 else
776 return 0;
778 return 1;
782 /***********************************************************************
783 * DRIVE_RawWrite
785 * Write raw sectors to a device
787 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
789 int fd;
791 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
793 lseek( fd, begin * 512, SEEK_SET );
794 /* FIXME: check errors */
795 write( fd, dataptr, nr_sect * 512 );
796 close( fd );
798 else
799 if (!(fake_success))
800 return 0;
802 return 1;
806 /***********************************************************************
807 * DRIVE_GetFreeSpace
809 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
810 PULARGE_INTEGER available )
812 struct statfs info;
813 unsigned long long bigsize,bigavail=0;
815 if (!DRIVE_IsValid(drive))
817 SetLastError( ERROR_INVALID_DRIVE );
818 return 0;
821 /* FIXME: add autoconf check for this */
822 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
823 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
824 #else
825 if (statfs( DOSDrives[drive].root, &info) < 0)
826 #endif
828 FILE_SetDosError();
829 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
830 return 0;
833 bigsize = (unsigned long long)info.f_bsize
834 * (unsigned long long)info.f_blocks;
835 #ifdef STATFS_HAS_BAVAIL
836 bigavail = (unsigned long long)info.f_bavail
837 * (unsigned long long)info.f_bsize;
838 #else
839 # ifdef STATFS_HAS_BFREE
840 bigavail = (unsigned long long)info.f_bfree
841 * (unsigned long long)info.f_bsize;
842 # else
843 # error "statfs has no bfree/bavail member!"
844 # endif
845 #endif
846 size->s.LowPart = (DWORD)bigsize;
847 size->s.HighPart = (DWORD)(bigsize>>32);
848 available->s.LowPart = (DWORD)bigavail;
849 available->s.HighPart = (DWORD)(bigavail>>32);
850 return 1;
853 /***********************************************************************
854 * DRIVE_GetCurrentDirectory
855 * Returns "X:\\path\\etc\\".
857 * Despite the API description, return required length including the
858 * terminating null when buffer too small. This is the real behaviour.
861 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
863 UINT ret;
864 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
866 assert(s);
867 ret = strlen(s) + 3; /* length of WHOLE current directory */
868 if (ret >= buflen) return ret + 1;
869 lstrcpynA( buf, "A:\\", MIN( 4, buflen ) );
870 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
871 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
872 return ret;
875 /***********************************************************************
876 * GetDiskFreeSpace16 (KERNEL.422)
878 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
879 LPDWORD sector_bytes, LPDWORD free_clusters,
880 LPDWORD total_clusters )
882 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
883 free_clusters, total_clusters );
887 /***********************************************************************
888 * GetDiskFreeSpace32A (KERNEL32.206)
890 * Fails if expression resulting from current drive's dir and "root"
891 * is not a root dir of the target drive.
893 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
894 * if the corresponding info is unneeded.
896 * FIXME: needs to support UNC names from Win95 OSR2 on.
898 * Behaviour under Win95a:
899 * CurrDir root result
900 * "E:\\TEST" "E:" FALSE
901 * "E:\\" "E:" TRUE
902 * "E:\\" "E" FALSE
903 * "E:\\" "\\" TRUE
904 * "E:\\TEST" "\\" TRUE
905 * "E:\\TEST" ":\\" FALSE
906 * "E:\\TEST" "E:\\" TRUE
907 * "E:\\TEST" "" FALSE
908 * "E:\\" "" FALSE (!)
909 * "E:\\" 0x0 TRUE
910 * "E:\\TEST" 0x0 TRUE (!)
911 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
912 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
914 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
915 LPDWORD sector_bytes, LPDWORD free_clusters,
916 LPDWORD total_clusters )
918 int drive;
919 ULARGE_INTEGER size,available;
920 LPCSTR path;
921 DWORD cluster_sec;
923 if ((!root) || (strcmp(root,"\\") == 0))
924 drive = DRIVE_GetCurrentDrive();
925 else
926 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
928 drive = toupper(root[0]) - 'A';
929 path = &root[2];
930 if (path[0] == '\0')
931 path = DRIVE_GetDosCwd(drive);
932 else
933 if (path[0] == '\\')
934 path++;
935 if (strlen(path)) /* oops, we are in a subdir */
936 return FALSE;
938 else
939 return FALSE;
941 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
943 /* Cap the size and available at 2GB as per specs. */
944 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
946 size.s.HighPart = 0;
947 size.s.LowPart = 0x7fffffff;
949 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
951 available.s.HighPart =0;
952 available.s.LowPart = 0x7fffffff;
954 if (DRIVE_GetType(drive)==TYPE_CDROM) {
955 if (sector_bytes)
956 *sector_bytes = 2048;
957 size.s.LowPart /= 2048;
958 available.s.LowPart /= 2048;
959 } else {
960 if (sector_bytes)
961 *sector_bytes = 512;
962 size.s.LowPart /= 512;
963 available.s.LowPart /= 512;
965 /* fixme: probably have to adjust those variables too for CDFS */
966 cluster_sec = 1;
967 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
969 if (cluster_sectors)
970 *cluster_sectors = cluster_sec;
971 if (free_clusters)
972 *free_clusters = available.s.LowPart / cluster_sec;
973 if (total_clusters)
974 *total_clusters = size.s.LowPart / cluster_sec;
975 return TRUE;
979 /***********************************************************************
980 * GetDiskFreeSpace32W (KERNEL32.207)
982 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
983 LPDWORD sector_bytes, LPDWORD free_clusters,
984 LPDWORD total_clusters )
986 LPSTR xroot;
987 BOOL ret;
989 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
990 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
991 free_clusters, total_clusters );
992 HeapFree( GetProcessHeap(), 0, xroot );
993 return ret;
997 /***********************************************************************
998 * GetDiskFreeSpaceEx32A (KERNEL32.871)
1000 * This function is used to aquire the size of the available and
1001 * total space on a logical volume.
1003 * RETURNS
1005 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1006 * detailed error information.
1009 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1010 PULARGE_INTEGER avail,
1011 PULARGE_INTEGER total,
1012 PULARGE_INTEGER totalfree)
1014 int drive;
1015 ULARGE_INTEGER size,available;
1017 if (!root) drive = DRIVE_GetCurrentDrive();
1018 else
1020 if ((root[1]) && ((root[1] != ':') || (root[2] != '\\')))
1022 FIXME("there are valid root names which are not supported yet\n");
1023 /* ..like UNC names, for instance. */
1025 WARN("invalid root '%s'\n", root );
1026 return FALSE;
1028 drive = toupper(root[0]) - 'A';
1031 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1033 if (total)
1035 total->s.HighPart = size.s.HighPart;
1036 total->s.LowPart = size.s.LowPart ;
1039 if (totalfree)
1041 totalfree->s.HighPart = available.s.HighPart;
1042 totalfree->s.LowPart = available.s.LowPart ;
1045 if (avail)
1047 if (FIXME_ON(dosfs))
1049 /* On Windows2000, we need to check the disk quota
1050 allocated for the user owning the calling process. We
1051 don't want to be more obtrusive than necessary with the
1052 FIXME messages, so don't print the FIXME unless Wine is
1053 actually masquerading as Windows2000. */
1055 OSVERSIONINFOA ovi;
1056 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1057 if (GetVersionExA(&ovi))
1059 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1060 FIXME("no per-user quota support yet\n");
1064 /* Quick hack, should eventually be fixed to work 100% with
1065 Windows2000 (see comment above). */
1066 avail->s.HighPart = available.s.HighPart;
1067 avail->s.LowPart = available.s.LowPart ;
1070 return TRUE;
1073 /***********************************************************************
1074 * GetDiskFreeSpaceEx32W (KERNEL32.873)
1076 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1077 PULARGE_INTEGER total,
1078 PULARGE_INTEGER totalfree)
1080 LPSTR xroot;
1081 BOOL ret;
1083 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1084 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1085 HeapFree( GetProcessHeap(), 0, xroot );
1086 return ret;
1089 /***********************************************************************
1090 * GetDriveType16 (KERNEL.136)
1091 * This function returns the type of a drive in Win16.
1092 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1093 * remote drive API. The returnvalue DRIVE_REMOTE for CD-ROMs has been
1094 * verified on Win3.11 and Windows 95. Some programs rely on it, so don't
1095 * do any pseudo-clever changes.
1097 * RETURNS
1098 * drivetype DRIVE_xxx
1100 UINT16 WINAPI GetDriveType16(
1101 UINT16 drive /* [in] number (NOT letter) of drive */
1103 TRACE("(%c:)\n", 'A' + drive );
1104 switch(DRIVE_GetType(drive))
1106 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
1107 case TYPE_HD: return DRIVE_FIXED;
1108 case TYPE_CDROM: return DRIVE_REMOTE;
1109 case TYPE_NETWORK: return DRIVE_REMOTE;
1110 case TYPE_INVALID:
1111 default: return DRIVE_CANNOTDETERMINE;
1116 /***********************************************************************
1117 * GetDriveType32A (KERNEL32.208)
1119 * Returns the type of the disk drive specified. If root is NULL the
1120 * root of the current directory is used.
1122 * RETURNS
1124 * Type of drive (from Win32 SDK):
1126 * DRIVE_UNKNOWN unable to find out anything about the drive
1127 * DRIVE_NO_ROOT_DIR nonexistand root dir
1128 * DRIVE_REMOVABLE the disk can be removed from the machine
1129 * DRIVE_FIXED the disk can not be removed from the machine
1130 * DRIVE_REMOTE network disk
1131 * DRIVE_CDROM CDROM drive
1132 * DRIVE_RAMDISK virtual disk in ram
1134 * DRIVE_DOESNOTEXIST XXX Not valid return value
1135 * DRIVE_CANNOTDETERMINE XXX Not valid return value
1137 * BUGS
1139 * Currently returns DRIVE_DOESNOTEXIST and DRIVE_CANNOTDETERMINE
1140 * when it really should return DRIVE_NO_ROOT_DIR and DRIVE_UNKNOWN.
1141 * Why were the former defines used?
1143 * DRIVE_RAMDISK is unsupported.
1145 UINT WINAPI GetDriveTypeA(LPCSTR root /* String describing drive */)
1147 int drive;
1148 TRACE("(%s)\n", debugstr_a(root));
1150 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1151 else
1153 if ((root[1]) && (root[1] != ':'))
1155 WARN("invalid root '%s'\n", debugstr_a(root));
1156 return DRIVE_DOESNOTEXIST;
1158 drive = toupper(root[0]) - 'A';
1160 switch(DRIVE_GetType(drive))
1162 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
1163 case TYPE_HD: return DRIVE_FIXED;
1164 case TYPE_CDROM: return DRIVE_CDROM;
1165 case TYPE_NETWORK: return DRIVE_REMOTE;
1166 case TYPE_INVALID: return DRIVE_DOESNOTEXIST;
1167 default: return DRIVE_CANNOTDETERMINE;
1172 /***********************************************************************
1173 * GetDriveType32W (KERNEL32.209)
1175 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1177 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1178 UINT ret = GetDriveTypeA( xpath );
1179 HeapFree( GetProcessHeap(), 0, xpath );
1180 return ret;
1184 /***********************************************************************
1185 * GetCurrentDirectory16 (KERNEL.411)
1187 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1189 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1193 /***********************************************************************
1194 * GetCurrentDirectory32A (KERNEL32.196)
1196 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1198 UINT ret;
1199 char longname[MAX_PATHNAME_LEN];
1200 char shortname[MAX_PATHNAME_LEN];
1201 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1202 if ( ret > MAX_PATHNAME_LEN ) {
1203 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1204 return ret;
1206 GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1207 ret = lstrlenA( longname ) + 1;
1208 if (ret > buflen) return ret;
1209 lstrcpyA(buf, longname);
1210 return ret - 1;
1213 /***********************************************************************
1214 * GetCurrentDirectory32W (KERNEL32.197)
1216 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1218 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1219 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1220 if (ret < buflen) lstrcpyAtoW ( buf, xpath );
1221 HeapFree( GetProcessHeap(), 0, xpath );
1222 return ret;
1226 /***********************************************************************
1227 * SetCurrentDirectory (KERNEL.412)
1229 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1231 return SetCurrentDirectoryA( dir );
1235 /***********************************************************************
1236 * SetCurrentDirectory32A (KERNEL32.479)
1238 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1240 int olddrive, drive = DRIVE_GetCurrentDrive();
1242 if (!dir) {
1243 ERR_(file)("(NULL)!\n");
1244 return FALSE;
1246 if (dir[0] && (dir[1]==':'))
1248 drive = tolower( *dir ) - 'a';
1249 dir += 2;
1252 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1253 sets pTask->curdir only if pTask->curdrive is drive */
1254 olddrive = drive; /* in case DRIVE_Chdir fails */
1255 if (!(DRIVE_SetCurrentDrive( drive )))
1256 return FALSE;
1257 /* FIXME: what about empty strings? Add a \\ ? */
1258 if (!DRIVE_Chdir( drive, dir )) {
1259 DRIVE_SetCurrentDrive(olddrive);
1260 return FALSE;
1262 return TRUE;
1266 /***********************************************************************
1267 * SetCurrentDirectory32W (KERNEL32.480)
1269 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1271 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1272 BOOL res = SetCurrentDirectoryA( dir );
1273 HeapFree( GetProcessHeap(), 0, dir );
1274 return res;
1278 /***********************************************************************
1279 * GetLogicalDriveStrings32A (KERNEL32.231)
1281 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1283 int drive, count;
1285 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1286 if (DRIVE_IsValid(drive)) count++;
1287 if ((count * 4) + 1 <= len)
1289 LPSTR p = buffer;
1290 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1291 if (DRIVE_IsValid(drive))
1293 *p++ = 'a' + drive;
1294 *p++ = ':';
1295 *p++ = '\\';
1296 *p++ = '\0';
1298 *p = '\0';
1299 return count * 4;
1301 else
1302 return (count * 4) + 1;/* account for terminating null */
1303 /* The API tells about these different return values */
1307 /***********************************************************************
1308 * GetLogicalDriveStrings32W (KERNEL32.232)
1310 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1312 int drive, count;
1314 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1315 if (DRIVE_IsValid(drive)) count++;
1316 if (count * 4 * sizeof(WCHAR) <= len)
1318 LPWSTR p = buffer;
1319 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1320 if (DRIVE_IsValid(drive))
1322 *p++ = (WCHAR)('a' + drive);
1323 *p++ = (WCHAR)':';
1324 *p++ = (WCHAR)'\\';
1325 *p++ = (WCHAR)'\0';
1327 *p = (WCHAR)'\0';
1329 return count * 4 * sizeof(WCHAR);
1333 /***********************************************************************
1334 * GetLogicalDrives (KERNEL32.233)
1336 DWORD WINAPI GetLogicalDrives(void)
1338 DWORD ret = 0;
1339 int drive;
1341 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1343 if ( (DRIVE_IsValid(drive)) ||
1344 (DOSDrives[drive].type == TYPE_CDROM)) /* audio CD is also valid */
1345 ret |= (1 << drive);
1347 return ret;
1351 /***********************************************************************
1352 * GetVolumeInformation32A (KERNEL32.309)
1354 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1355 DWORD label_len, DWORD *serial,
1356 DWORD *filename_len, DWORD *flags,
1357 LPSTR fsname, DWORD fsname_len )
1359 int drive;
1360 char *cp;
1362 /* FIXME, SetLastError()s missing */
1364 if (!root) drive = DRIVE_GetCurrentDrive();
1365 else
1367 if ((root[1]) && (root[1] != ':'))
1369 WARN("invalid root '%s'\n",root);
1370 return FALSE;
1372 drive = toupper(root[0]) - 'A';
1374 if (!DRIVE_IsValid( drive )) return FALSE;
1375 if (label)
1377 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1378 for (cp = label; *cp; cp++);
1379 while (cp != label && *(cp-1) == ' ') cp--;
1380 *cp = '\0';
1382 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1384 /* Set the filesystem information */
1385 /* Note: we only emulate a FAT fs at present */
1387 if (filename_len) {
1388 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1389 *filename_len = 12;
1390 else
1391 *filename_len = 255;
1393 if (flags)
1395 *flags=0;
1396 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1397 *flags|=FS_CASE_SENSITIVE;
1398 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1399 *flags|=FS_CASE_IS_PRESERVED ;
1401 if (fsname) {
1402 /* Diablo checks that return code ... */
1403 if (DRIVE_GetType(drive)==TYPE_CDROM)
1404 lstrcpynA( fsname, "CDFS", fsname_len );
1405 else
1406 lstrcpynA( fsname, "FAT", fsname_len );
1408 return TRUE;
1412 /***********************************************************************
1413 * GetVolumeInformation32W (KERNEL32.310)
1415 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1416 DWORD label_len, DWORD *serial,
1417 DWORD *filename_len, DWORD *flags,
1418 LPWSTR fsname, DWORD fsname_len )
1420 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1421 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1422 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1423 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1424 filename_len, flags, xfsname,
1425 fsname_len );
1426 if (ret)
1428 if (label) lstrcpyAtoW( label, xvolname );
1429 if (fsname) lstrcpyAtoW( fsname, xfsname );
1431 HeapFree( GetProcessHeap(), 0, xroot );
1432 HeapFree( GetProcessHeap(), 0, xvolname );
1433 HeapFree( GetProcessHeap(), 0, xfsname );
1434 return ret;
1437 /***********************************************************************
1438 * SetVolumeLabelA (KERNEL32.675)
1440 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1442 int drive;
1444 /* FIXME, SetLastErrors missing */
1446 if (!root) drive = DRIVE_GetCurrentDrive();
1447 else
1449 if ((root[1]) && (root[1] != ':'))
1451 WARN("invalid root '%s'\n",root);
1452 return FALSE;
1454 drive = toupper(root[0]) - 'A';
1456 if (!DRIVE_IsValid( drive )) return FALSE;
1458 /* some copy protection stuff check this */
1459 if (DRIVE_GetType( drive ) == TYPE_CDROM) return FALSE;
1461 FIXME("(%s,%s),stub!\n", root, volname);
1462 return TRUE;
1465 /***********************************************************************
1466 * SetVolumeLabelW (KERNEL32.676)
1468 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1470 LPSTR xroot, xvol;
1471 BOOL ret;
1473 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1474 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1475 ret = SetVolumeLabelA( xroot, xvol );
1476 HeapFree( GetProcessHeap(), 0, xroot );
1477 HeapFree( GetProcessHeap(), 0, xvol );
1478 return ret;