Drop UNIX cwd to / after calculating WINE current directory (to allow
[wine/wine-kai.git] / files / drive.c
blobe8ab1a02fdd0ac765343f45050a60d259abfb20c
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].flags = 0;
243 DRIVE_CurDrive = 2;
246 /* Make sure the current drive is valid */
247 if (DRIVE_CurDrive == -1)
249 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
251 if (drive->root && !(drive->flags & DRIVE_DISABLED))
253 DRIVE_CurDrive = i;
254 break;
259 return 1;
263 /***********************************************************************
264 * DRIVE_IsValid
266 int DRIVE_IsValid( int drive )
268 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
269 return (DOSDrives[drive].root &&
270 !(DOSDrives[drive].flags & DRIVE_DISABLED));
274 /***********************************************************************
275 * DRIVE_GetCurrentDrive
277 int DRIVE_GetCurrentDrive(void)
279 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
280 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
281 return DRIVE_CurDrive;
285 /***********************************************************************
286 * DRIVE_SetCurrentDrive
288 int DRIVE_SetCurrentDrive( int drive )
290 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
291 if (!DRIVE_IsValid( drive ))
293 SetLastError( ERROR_INVALID_DRIVE );
294 return 0;
296 TRACE("%c:\n", 'A' + drive );
297 DRIVE_CurDrive = drive;
298 if (pTask) pTask->curdrive = drive | 0x80;
299 return 1;
303 /***********************************************************************
304 * DRIVE_FindDriveRoot
306 * Find a drive for which the root matches the beginning of the given path.
307 * This can be used to translate a Unix path into a drive + DOS path.
308 * Return value is the drive, or -1 on error. On success, path is modified
309 * to point to the beginning of the DOS path.
311 int DRIVE_FindDriveRoot( const char **path )
313 /* idea: check at all '/' positions.
314 * If the device and inode of that path is identical with the
315 * device and inode of the current drive then we found a solution.
316 * If there is another drive pointing to a deeper position in
317 * the file tree, we want to find that one, not the earlier solution.
319 int drive, rootdrive = -1;
320 char buffer[MAX_PATHNAME_LEN];
321 char *next = buffer;
322 const char *p = *path;
323 struct stat st;
325 strcpy( buffer, "/" );
326 for (;;)
328 if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
330 /* Find the drive */
332 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
334 if (!DOSDrives[drive].root ||
335 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
337 if ((DOSDrives[drive].dev == st.st_dev) &&
338 (DOSDrives[drive].ino == st.st_ino))
340 rootdrive = drive;
341 *path = p;
342 break;
346 /* Get the next path component */
348 *next++ = '/';
349 while ((*p == '/') || (*p == '\\')) p++;
350 if (!*p) break;
351 while (!IS_END_OF_NAME(*p)) *next++ = *p++;
352 *next = 0;
354 *next = 0;
356 if (rootdrive != -1)
357 TRACE("%s -> drive %c:, root='%s', name='%s'\n",
358 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, *path );
359 return rootdrive;
363 /***********************************************************************
364 * DRIVE_GetRoot
366 const char * DRIVE_GetRoot( int drive )
368 if (!DRIVE_IsValid( drive )) return NULL;
369 return DOSDrives[drive].root;
373 /***********************************************************************
374 * DRIVE_GetDosCwd
376 const char * DRIVE_GetDosCwd( int drive )
378 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
379 if (!DRIVE_IsValid( drive )) return NULL;
381 /* Check if we need to change the directory to the new task. */
382 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
383 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
384 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
386 /* Perform the task-switch */
387 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
388 DRIVE_LastTask = GetCurrentTask();
390 return DOSDrives[drive].dos_cwd;
394 /***********************************************************************
395 * DRIVE_GetUnixCwd
397 const char * DRIVE_GetUnixCwd( int drive )
399 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
400 if (!DRIVE_IsValid( drive )) return NULL;
402 /* Check if we need to change the directory to the new task. */
403 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
404 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
405 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
407 /* Perform the task-switch */
408 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
409 DRIVE_LastTask = GetCurrentTask();
411 return DOSDrives[drive].unix_cwd;
415 /***********************************************************************
416 * DRIVE_GetDevice
418 const char * DRIVE_GetDevice( int drive )
420 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
424 /***********************************************************************
425 * DRIVE_ReadSuperblock
427 * NOTE
428 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
429 * to check, that they are writing on a FAT filesystem !
431 int DRIVE_ReadSuperblock (int drive, char * buff)
433 #define DRIVE_SUPER 96
434 int fd;
435 off_t offs;
437 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
438 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
440 struct stat st;
441 if (!DOSDrives[drive].device)
442 ERR("No device configured for drive %c: !\n", 'A'+drive);
443 else
444 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
445 (stat(DOSDrives[drive].device, &st)) ?
446 "not available or symlink not valid ?" : "no permission");
447 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
448 PROFILE_UsageWineIni();
449 return -1;
452 switch(DOSDrives[drive].type)
454 case TYPE_FLOPPY:
455 case TYPE_HD:
456 offs = 0;
457 break;
458 case TYPE_CDROM:
459 /* FIXME: Maybe we should search for the first data track on the CD,
460 not just assume that it is the first track */
461 offs = (off_t)2048*(16+0);
462 break;
463 default:
464 offs = 0;
465 break;
468 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs)) return -4;
469 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER) return -2;
471 switch(DOSDrives[drive].type)
473 case TYPE_FLOPPY:
474 case TYPE_HD:
475 if ((buff[0x26]!=0x29) || /* Check for FAT present */
476 /* FIXME: do really all Fat have their name beginning with
477 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
478 memcmp( buff+0x36,"FAT",3))
480 ERR("The filesystem is not FAT !! (device=%s)\n",
481 DOSDrives[drive].device);
482 return -3;
484 break;
485 case TYPE_CDROM:
486 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
487 return -3;
488 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
489 break;
490 default:
491 return -3;
492 break;
495 return close(fd);
499 /***********************************************************************
500 * DRIVE_WriteSuperblockEntry
502 * NOTE
503 * We are writing as little as possible (ie. not the whole SuperBlock)
504 * not to interfere with kernel. The drive can be mounted !
506 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
508 int fd;
510 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
512 ERR("Cannot open the device %s (for writing)\n",
513 DOSDrives[drive].device);
514 return -1;
516 if (lseek(fd,ofs,SEEK_SET)!=ofs)
518 ERR("lseek failed on device %s !\n",
519 DOSDrives[drive].device);
520 close(fd);
521 return -2;
523 if (write(fd,buff,len)!=len)
525 close(fd);
526 ERR("Cannot write on %s !\n",
527 DOSDrives[drive].device);
528 return -3;
530 return close (fd);
535 /***********************************************************************
536 * DRIVE_GetLabel
538 const char * DRIVE_GetLabel( int drive )
540 int read = 0;
541 char buff[DRIVE_SUPER];
542 int offs = -1;
544 if (!DRIVE_IsValid( drive )) return NULL;
545 if (DRIVE_GetType(drive) == TYPE_CDROM)
547 WINE_CDAUDIO wcda;
549 if (!(CDROM_Open(&wcda, drive)))
551 int media = CDROM_GetMediaType(&wcda);
553 if (media == CDS_AUDIO)
555 strcpy(DOSDrives[drive].label_read, "Audio CD ");
556 read = 1;
558 else
559 if (media == CDS_NO_INFO)
561 strcpy(DOSDrives[drive].label_read, " ");
562 read = 1;
565 CDROM_Close(&wcda);
568 if ((!read) && (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO))
570 if (DRIVE_ReadSuperblock(drive,(char *) buff))
571 ERR("Invalid or unreadable superblock on %s (%c:).\n",
572 DOSDrives[drive].device, (char)(drive+'A'));
573 else {
574 if (DOSDrives[drive].type == TYPE_CDROM)
575 offs = 40;
576 else
577 if (DOSDrives[drive].type == TYPE_FLOPPY ||
578 DOSDrives[drive].type == TYPE_HD)
579 offs = 0x2b;
581 /* FIXME: ISO9660 uses 32-bytes long label. Should we do also? */
582 if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
583 DOSDrives[drive].label_read[11]='\0';
584 read = 1;
588 return (read) ?
589 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
593 /***********************************************************************
594 * DRIVE_GetSerialNumber
596 DWORD DRIVE_GetSerialNumber( int drive )
598 DWORD serial = 0;
599 char buff[DRIVE_SUPER];
601 if (!DRIVE_IsValid( drive )) return 0;
603 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
605 switch(DOSDrives[drive].type)
607 case TYPE_FLOPPY:
608 case TYPE_HD:
609 if (DRIVE_ReadSuperblock(drive,(char *) buff))
610 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
611 " Maybe not FAT?\n" ,
612 DOSDrives[drive].device, 'A'+drive);
613 else
614 serial = *((DWORD*)(buff+0x27));
615 break;
616 case TYPE_CDROM:
617 serial = CDROM_GetSerial(drive);
618 break;
619 default:
620 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
624 return (serial) ? serial : DOSDrives[drive].serial_conf;
628 /***********************************************************************
629 * DRIVE_SetSerialNumber
631 int DRIVE_SetSerialNumber( int drive, DWORD serial )
633 char buff[DRIVE_SUPER];
635 if (!DRIVE_IsValid( drive )) return 0;
637 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
639 if ((DOSDrives[drive].type != TYPE_FLOPPY) &&
640 (DOSDrives[drive].type != TYPE_HD)) return 0;
641 /* check, if the drive has a FAT filesystem */
642 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
643 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
644 return 1;
647 if (DOSDrives[drive].type == TYPE_CDROM) return 0;
648 DOSDrives[drive].serial_conf = serial;
649 return 1;
653 /***********************************************************************
654 * DRIVE_GetType
656 DRIVETYPE DRIVE_GetType( int drive )
658 if (!DRIVE_IsValid( drive )) return TYPE_INVALID;
659 return DOSDrives[drive].type;
663 /***********************************************************************
664 * DRIVE_GetFlags
666 UINT DRIVE_GetFlags( int drive )
668 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
669 return DOSDrives[drive].flags;
673 /***********************************************************************
674 * DRIVE_Chdir
676 int DRIVE_Chdir( int drive, const char *path )
678 DOS_FULL_NAME full_name;
679 char buffer[MAX_PATHNAME_LEN];
680 LPSTR unix_cwd;
681 BY_HANDLE_FILE_INFORMATION info;
682 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
684 strcpy( buffer, "A:" );
685 buffer[0] += drive;
686 TRACE("(%c:,%s)\n", buffer[0], path );
687 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
689 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
690 if (!FILE_Stat( full_name.long_name, &info )) return 0;
691 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
693 SetLastError( ERROR_FILE_NOT_FOUND );
694 return 0;
696 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
697 while (*unix_cwd == '/') unix_cwd++;
699 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
700 'A' + drive, unix_cwd, full_name.short_name + 3 );
702 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
703 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
704 DOSDrives[drive].dos_cwd = HEAP_strdupA( GetProcessHeap(), 0,
705 full_name.short_name + 3 );
706 DOSDrives[drive].unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, unix_cwd );
708 if (pTask && (pTask->curdrive & 0x80) &&
709 ((pTask->curdrive & ~0x80) == drive))
711 lstrcpynA( pTask->curdir, full_name.short_name + 2,
712 sizeof(pTask->curdir) );
713 DRIVE_LastTask = GetCurrentTask();
715 return 1;
719 /***********************************************************************
720 * DRIVE_Disable
722 int DRIVE_Disable( int drive )
724 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
726 SetLastError( ERROR_INVALID_DRIVE );
727 return 0;
729 DOSDrives[drive].flags |= DRIVE_DISABLED;
730 return 1;
734 /***********************************************************************
735 * DRIVE_Enable
737 int DRIVE_Enable( int drive )
739 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
741 SetLastError( ERROR_INVALID_DRIVE );
742 return 0;
744 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
745 return 1;
749 /***********************************************************************
750 * DRIVE_SetLogicalMapping
752 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
754 /* If new_drive is already valid, do nothing and return 0
755 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
757 DOSDRIVE *old, *new;
759 old = DOSDrives + existing_drive;
760 new = DOSDrives + new_drive;
762 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
763 !old->root ||
764 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
766 SetLastError( ERROR_INVALID_DRIVE );
767 return 0;
770 if ( new->root )
772 TRACE("Can\'t map drive %c to drive %c - drive %c already exists\n",
773 'A' + existing_drive, 'A' + new_drive, 'A' + new_drive );
774 /* it is already mapped there, so return success */
775 if (!strcmp(old->root,new->root))
776 return 1;
777 return 0;
780 new->root = HEAP_strdupA( GetProcessHeap(), 0, old->root );
781 new->dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->dos_cwd );
782 new->unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->unix_cwd );
783 memcpy ( new->label_conf, old->label_conf, 12 );
784 new->serial_conf = old->serial_conf;
785 new->type = old->type;
786 new->flags = old->flags;
787 new->dev = old->dev;
788 new->ino = old->ino;
790 TRACE("Drive %c is now equal to drive %c\n",
791 'A' + new_drive, 'A' + existing_drive );
793 return 1;
797 /***********************************************************************
798 * DRIVE_OpenDevice
800 * Open the drive raw device and return a Unix fd (or -1 on error).
802 int DRIVE_OpenDevice( int drive, int flags )
804 if (!DRIVE_IsValid( drive )) return -1;
805 return open( DOSDrives[drive].device, flags );
809 /***********************************************************************
810 * DRIVE_RawRead
812 * Read raw sectors from a device
814 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
816 int fd;
818 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
820 lseek( fd, begin * 512, SEEK_SET );
821 /* FIXME: check errors */
822 read( fd, dataptr, nr_sect * 512 );
823 close( fd );
825 else
827 memset(dataptr, 0, nr_sect * 512);
828 if (fake_success)
830 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
831 if (begin == 1) *dataptr = 0xf8;
833 else
834 return 0;
836 return 1;
840 /***********************************************************************
841 * DRIVE_RawWrite
843 * Write raw sectors to a device
845 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
847 int fd;
849 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
851 lseek( fd, begin * 512, SEEK_SET );
852 /* FIXME: check errors */
853 write( fd, dataptr, nr_sect * 512 );
854 close( fd );
856 else
857 if (!(fake_success))
858 return 0;
860 return 1;
864 /***********************************************************************
865 * DRIVE_GetFreeSpace
867 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
868 PULARGE_INTEGER available )
870 struct statfs info;
871 unsigned long long bigsize,bigavail=0;
873 if (!DRIVE_IsValid(drive))
875 SetLastError( ERROR_INVALID_DRIVE );
876 return 0;
879 /* FIXME: add autoconf check for this */
880 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
881 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
882 #else
883 if (statfs( DOSDrives[drive].root, &info) < 0)
884 #endif
886 FILE_SetDosError();
887 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
888 return 0;
891 bigsize = (unsigned long long)info.f_bsize
892 * (unsigned long long)info.f_blocks;
893 #ifdef STATFS_HAS_BAVAIL
894 bigavail = (unsigned long long)info.f_bavail
895 * (unsigned long long)info.f_bsize;
896 #else
897 # ifdef STATFS_HAS_BFREE
898 bigavail = (unsigned long long)info.f_bfree
899 * (unsigned long long)info.f_bsize;
900 # else
901 # error "statfs has no bfree/bavail member!"
902 # endif
903 #endif
904 size->s.LowPart = (DWORD)bigsize;
905 size->s.HighPart = (DWORD)(bigsize>>32);
906 if (DRIVE_GetType(drive) == TYPE_CDROM)
907 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
908 available->s.LowPart = 0;
909 available->s.HighPart = 0;
911 else
913 available->s.LowPart = (DWORD)bigavail;
914 available->s.HighPart = (DWORD)(bigavail>>32);
916 return 1;
919 /***********************************************************************
920 * DRIVE_GetCurrentDirectory
921 * Returns "X:\\path\\etc\\".
923 * Despite the API description, return required length including the
924 * terminating null when buffer too small. This is the real behaviour.
927 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
929 UINT ret;
930 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
932 assert(s);
933 ret = strlen(s) + 3; /* length of WHOLE current directory */
934 if (ret >= buflen) return ret + 1;
935 lstrcpynA( buf, "A:\\", min( 4, buflen ) );
936 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
937 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
938 return ret;
941 /***********************************************************************
942 * GetDiskFreeSpace16 (KERNEL.422)
944 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
945 LPDWORD sector_bytes, LPDWORD free_clusters,
946 LPDWORD total_clusters )
948 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
949 free_clusters, total_clusters );
953 /***********************************************************************
954 * GetDiskFreeSpaceA (KERNEL32.206)
956 * Fails if expression resulting from current drive's dir and "root"
957 * is not a root dir of the target drive.
959 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
960 * if the corresponding info is unneeded.
962 * FIXME: needs to support UNC names from Win95 OSR2 on.
964 * Behaviour under Win95a:
965 * CurrDir root result
966 * "E:\\TEST" "E:" FALSE
967 * "E:\\" "E:" TRUE
968 * "E:\\" "E" FALSE
969 * "E:\\" "\\" TRUE
970 * "E:\\TEST" "\\" TRUE
971 * "E:\\TEST" ":\\" FALSE
972 * "E:\\TEST" "E:\\" TRUE
973 * "E:\\TEST" "" FALSE
974 * "E:\\" "" FALSE (!)
975 * "E:\\" 0x0 TRUE
976 * "E:\\TEST" 0x0 TRUE (!)
977 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
978 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
980 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
981 LPDWORD sector_bytes, LPDWORD free_clusters,
982 LPDWORD total_clusters )
984 int drive, sec_size;
985 ULARGE_INTEGER size,available;
986 LPCSTR path;
987 DWORD cluster_sec;
989 if ((!root) || (strcmp(root,"\\") == 0))
990 drive = DRIVE_GetCurrentDrive();
991 else
992 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
994 drive = toupper(root[0]) - 'A';
995 path = &root[2];
996 if (path[0] == '\0')
997 path = DRIVE_GetDosCwd(drive);
998 else
999 if (path[0] == '\\')
1000 path++;
1001 if (strlen(path)) /* oops, we are in a subdir */
1002 return FALSE;
1004 else
1005 return FALSE;
1007 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1009 /* Cap the size and available at 2GB as per specs. */
1010 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1012 size.s.HighPart = 0;
1013 size.s.LowPart = 0x7fffffff;
1015 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1017 available.s.HighPart =0;
1018 available.s.LowPart = 0x7fffffff;
1020 sec_size = (DRIVE_GetType(drive)==TYPE_CDROM) ? 2048 : 512;
1021 size.s.LowPart /= sec_size;
1022 available.s.LowPart /= sec_size;
1023 /* fixme: probably have to adjust those variables too for CDFS */
1024 cluster_sec = 1;
1025 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1027 if (cluster_sectors)
1028 *cluster_sectors = cluster_sec;
1029 if (sector_bytes)
1030 *sector_bytes = sec_size;
1031 if (free_clusters)
1032 *free_clusters = available.s.LowPart / cluster_sec;
1033 if (total_clusters)
1034 *total_clusters = size.s.LowPart / cluster_sec;
1035 return TRUE;
1039 /***********************************************************************
1040 * GetDiskFreeSpaceW (KERNEL32.207)
1042 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1043 LPDWORD sector_bytes, LPDWORD free_clusters,
1044 LPDWORD total_clusters )
1046 LPSTR xroot;
1047 BOOL ret;
1049 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1050 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
1051 free_clusters, total_clusters );
1052 HeapFree( GetProcessHeap(), 0, xroot );
1053 return ret;
1057 /***********************************************************************
1058 * GetDiskFreeSpaceExA (KERNEL32.871)
1060 * This function is used to aquire the size of the available and
1061 * total space on a logical volume.
1063 * RETURNS
1065 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1066 * detailed error information.
1069 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1070 PULARGE_INTEGER avail,
1071 PULARGE_INTEGER total,
1072 PULARGE_INTEGER totalfree)
1074 int drive;
1075 ULARGE_INTEGER size,available;
1077 if (!root) drive = DRIVE_GetCurrentDrive();
1078 else
1080 if ((root[1]) && ((root[1] != ':') || (root[2] != '\\')))
1082 FIXME("there are valid root names which are not supported yet\n");
1083 /* ..like UNC names, for instance. */
1085 WARN("invalid root '%s'\n", root );
1086 return FALSE;
1088 drive = toupper(root[0]) - 'A';
1091 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1093 if (total)
1095 total->s.HighPart = size.s.HighPart;
1096 total->s.LowPart = size.s.LowPart ;
1099 if (totalfree)
1101 totalfree->s.HighPart = available.s.HighPart;
1102 totalfree->s.LowPart = available.s.LowPart ;
1105 if (avail)
1107 if (FIXME_ON(dosfs))
1109 /* On Windows2000, we need to check the disk quota
1110 allocated for the user owning the calling process. We
1111 don't want to be more obtrusive than necessary with the
1112 FIXME messages, so don't print the FIXME unless Wine is
1113 actually masquerading as Windows2000. */
1115 OSVERSIONINFOA ovi;
1116 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1117 if (GetVersionExA(&ovi))
1119 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1120 FIXME("no per-user quota support yet\n");
1124 /* Quick hack, should eventually be fixed to work 100% with
1125 Windows2000 (see comment above). */
1126 avail->s.HighPart = available.s.HighPart;
1127 avail->s.LowPart = available.s.LowPart ;
1130 return TRUE;
1133 /***********************************************************************
1134 * GetDiskFreeSpaceExW (KERNEL32.873)
1136 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1137 PULARGE_INTEGER total,
1138 PULARGE_INTEGER totalfree)
1140 LPSTR xroot;
1141 BOOL ret;
1143 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1144 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1145 HeapFree( GetProcessHeap(), 0, xroot );
1146 return ret;
1149 /***********************************************************************
1150 * GetDriveType16 (KERNEL.136)
1151 * This function returns the type of a drive in Win16.
1152 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1153 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1154 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1155 * do any pseudo-clever changes.
1157 * RETURNS
1158 * drivetype DRIVE_xxx
1160 UINT16 WINAPI GetDriveType16(
1161 UINT16 drive /* [in] number (NOT letter) of drive */
1163 TRACE("(%c:)\n", 'A' + drive );
1164 switch(DRIVE_GetType(drive))
1166 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
1167 case TYPE_HD: return DRIVE_FIXED;
1168 case TYPE_CDROM: return DRIVE_REMOTE;
1169 case TYPE_NETWORK: return DRIVE_REMOTE;
1170 case TYPE_INVALID:
1171 default: return DRIVE_CANNOTDETERMINE;
1176 /***********************************************************************
1177 * GetDriveTypeA (KERNEL32.208)
1179 * Returns the type of the disk drive specified. If root is NULL the
1180 * root of the current directory is used.
1182 * RETURNS
1184 * Type of drive (from Win32 SDK):
1186 * DRIVE_UNKNOWN unable to find out anything about the drive
1187 * DRIVE_NO_ROOT_DIR nonexistand root dir
1188 * DRIVE_REMOVABLE the disk can be removed from the machine
1189 * DRIVE_FIXED the disk can not be removed from the machine
1190 * DRIVE_REMOTE network disk
1191 * DRIVE_CDROM CDROM drive
1192 * DRIVE_RAMDISK virtual disk in ram
1194 * DRIVE_DOESNOTEXIST XXX Not valid return value
1195 * DRIVE_CANNOTDETERMINE XXX Not valid return value
1197 * BUGS
1199 * Currently returns DRIVE_DOESNOTEXIST and DRIVE_CANNOTDETERMINE
1200 * when it really should return DRIVE_NO_ROOT_DIR and DRIVE_UNKNOWN.
1201 * Why were the former defines used?
1203 * DRIVE_RAMDISK is unsupported.
1205 UINT WINAPI GetDriveTypeA(LPCSTR root /* String describing drive */)
1207 int drive;
1208 TRACE("(%s)\n", debugstr_a(root));
1210 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1211 else
1213 if ((root[1]) && (root[1] != ':'))
1215 WARN("invalid root '%s'\n", debugstr_a(root));
1216 return DRIVE_DOESNOTEXIST;
1218 drive = toupper(root[0]) - 'A';
1220 switch(DRIVE_GetType(drive))
1222 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
1223 case TYPE_HD: return DRIVE_FIXED;
1224 case TYPE_CDROM: return DRIVE_CDROM;
1225 case TYPE_NETWORK: return DRIVE_REMOTE;
1226 case TYPE_INVALID: return DRIVE_DOESNOTEXIST;
1227 default: return DRIVE_CANNOTDETERMINE;
1232 /***********************************************************************
1233 * GetDriveTypeW (KERNEL32.209)
1235 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1237 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1238 UINT ret = GetDriveTypeA( xpath );
1239 HeapFree( GetProcessHeap(), 0, xpath );
1240 return ret;
1244 /***********************************************************************
1245 * GetCurrentDirectory16 (KERNEL.411)
1247 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1249 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1253 /***********************************************************************
1254 * GetCurrentDirectoryA (KERNEL32.196)
1256 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1258 UINT ret;
1259 char longname[MAX_PATHNAME_LEN];
1260 char shortname[MAX_PATHNAME_LEN];
1261 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1262 if ( ret > MAX_PATHNAME_LEN ) {
1263 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1264 return ret;
1266 GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1267 ret = lstrlenA( longname ) + 1;
1268 if (ret > buflen) return ret;
1269 lstrcpyA(buf, longname);
1270 return ret - 1;
1273 /***********************************************************************
1274 * GetCurrentDirectoryW (KERNEL32.197)
1276 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1278 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1279 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1280 if (ret < buflen) lstrcpyAtoW ( buf, xpath );
1281 HeapFree( GetProcessHeap(), 0, xpath );
1282 return ret;
1286 /***********************************************************************
1287 * SetCurrentDirectory (KERNEL.412)
1289 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1291 return SetCurrentDirectoryA( dir );
1295 /***********************************************************************
1296 * SetCurrentDirectoryA (KERNEL32.479)
1298 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1300 int olddrive, drive = DRIVE_GetCurrentDrive();
1302 if (!dir) {
1303 ERR_(file)("(NULL)!\n");
1304 return FALSE;
1306 if (dir[0] && (dir[1]==':'))
1308 drive = tolower( *dir ) - 'a';
1309 dir += 2;
1312 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1313 sets pTask->curdir only if pTask->curdrive is drive */
1314 olddrive = drive; /* in case DRIVE_Chdir fails */
1315 if (!(DRIVE_SetCurrentDrive( drive )))
1316 return FALSE;
1317 /* FIXME: what about empty strings? Add a \\ ? */
1318 if (!DRIVE_Chdir( drive, dir )) {
1319 DRIVE_SetCurrentDrive(olddrive);
1320 return FALSE;
1322 return TRUE;
1326 /***********************************************************************
1327 * SetCurrentDirectoryW (KERNEL32.480)
1329 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1331 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1332 BOOL res = SetCurrentDirectoryA( dir );
1333 HeapFree( GetProcessHeap(), 0, dir );
1334 return res;
1338 /***********************************************************************
1339 * GetLogicalDriveStringsA (KERNEL32.231)
1341 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1343 int drive, count;
1345 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1346 if (DRIVE_IsValid(drive)) count++;
1347 if ((count * 4) + 1 <= len)
1349 LPSTR p = buffer;
1350 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1351 if (DRIVE_IsValid(drive))
1353 *p++ = 'a' + drive;
1354 *p++ = ':';
1355 *p++ = '\\';
1356 *p++ = '\0';
1358 *p = '\0';
1359 return count * 4;
1361 else
1362 return (count * 4) + 1;/* account for terminating null */
1363 /* The API tells about these different return values */
1367 /***********************************************************************
1368 * GetLogicalDriveStringsW (KERNEL32.232)
1370 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1372 int drive, count;
1374 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1375 if (DRIVE_IsValid(drive)) count++;
1376 if (count * 4 * sizeof(WCHAR) <= len)
1378 LPWSTR p = buffer;
1379 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1380 if (DRIVE_IsValid(drive))
1382 *p++ = (WCHAR)('a' + drive);
1383 *p++ = (WCHAR)':';
1384 *p++ = (WCHAR)'\\';
1385 *p++ = (WCHAR)'\0';
1387 *p = (WCHAR)'\0';
1389 return count * 4 * sizeof(WCHAR);
1393 /***********************************************************************
1394 * GetLogicalDrives (KERNEL32.233)
1396 DWORD WINAPI GetLogicalDrives(void)
1398 DWORD ret = 0;
1399 int drive;
1401 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1403 if ( (DRIVE_IsValid(drive)) ||
1404 (DOSDrives[drive].type == TYPE_CDROM)) /* audio CD is also valid */
1405 ret |= (1 << drive);
1407 return ret;
1411 /***********************************************************************
1412 * GetVolumeInformationA (KERNEL32.309)
1414 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1415 DWORD label_len, DWORD *serial,
1416 DWORD *filename_len, DWORD *flags,
1417 LPSTR fsname, DWORD fsname_len )
1419 int drive;
1420 char *cp;
1422 /* FIXME, SetLastError()s missing */
1424 if (!root) drive = DRIVE_GetCurrentDrive();
1425 else
1427 if ((root[1]) && (root[1] != ':'))
1429 WARN("invalid root '%s'\n",root);
1430 return FALSE;
1432 drive = toupper(root[0]) - 'A';
1434 if (!DRIVE_IsValid( drive )) return FALSE;
1435 if (label)
1437 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1438 for (cp = label; *cp; cp++);
1439 while (cp != label && *(cp-1) == ' ') cp--;
1440 *cp = '\0';
1442 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1444 /* Set the filesystem information */
1445 /* Note: we only emulate a FAT fs at present */
1447 if (filename_len) {
1448 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1449 *filename_len = 12;
1450 else
1451 *filename_len = 255;
1453 if (flags)
1455 *flags=0;
1456 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1457 *flags|=FS_CASE_SENSITIVE;
1458 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1459 *flags|=FS_CASE_IS_PRESERVED;
1461 if (fsname) {
1462 /* Diablo checks that return code ... */
1463 if (DRIVE_GetType(drive)==TYPE_CDROM)
1464 lstrcpynA( fsname, "CDFS", fsname_len );
1465 else
1466 lstrcpynA( fsname, "FAT", fsname_len );
1468 return TRUE;
1472 /***********************************************************************
1473 * GetVolumeInformationW (KERNEL32.310)
1475 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1476 DWORD label_len, DWORD *serial,
1477 DWORD *filename_len, DWORD *flags,
1478 LPWSTR fsname, DWORD fsname_len )
1480 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1481 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1482 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1483 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1484 filename_len, flags, xfsname,
1485 fsname_len );
1486 if (ret)
1488 if (label) lstrcpyAtoW( label, xvolname );
1489 if (fsname) lstrcpyAtoW( fsname, xfsname );
1491 HeapFree( GetProcessHeap(), 0, xroot );
1492 HeapFree( GetProcessHeap(), 0, xvolname );
1493 HeapFree( GetProcessHeap(), 0, xfsname );
1494 return ret;
1497 /***********************************************************************
1498 * SetVolumeLabelA (KERNEL32.675)
1500 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1502 int drive;
1504 /* FIXME, SetLastErrors missing */
1506 if (!root) drive = DRIVE_GetCurrentDrive();
1507 else
1509 if ((root[1]) && (root[1] != ':'))
1511 WARN("invalid root '%s'\n",root);
1512 return FALSE;
1514 drive = toupper(root[0]) - 'A';
1516 if (!DRIVE_IsValid( drive )) return FALSE;
1518 /* some copy protection stuff check this */
1519 if (DRIVE_GetType( drive ) == TYPE_CDROM) return FALSE;
1521 FIXME("(%s,%s),stub!\n", root, volname);
1522 return TRUE;
1525 /***********************************************************************
1526 * SetVolumeLabelW (KERNEL32.676)
1528 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1530 LPSTR xroot, xvol;
1531 BOOL ret;
1533 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1534 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1535 ret = SetVolumeLabelA( xroot, xvol );
1536 HeapFree( GetProcessHeap(), 0, xroot );
1537 HeapFree( GetProcessHeap(), 0, xvol );
1538 return ret;