Fixed segmented/linear buffers manipulation.
[wine.git] / files / drive.c
blob5dc15b431b2934eb6d257984092498ffda95697e
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;
341 break;
345 /* Get the next path component */
347 *next++ = '/';
348 while ((*p == '/') || (*p == '\\')) p++;
349 if (!*p) break;
350 while (!IS_END_OF_NAME(*p)) *next++ = *p++;
351 *next = 0;
353 *next = 0;
355 if (rootdrive != -1)
356 TRACE("%s -> drive %c:, root='%s', name='%s'\n",
357 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, *path );
358 return rootdrive;
362 /***********************************************************************
363 * DRIVE_GetRoot
365 const char * DRIVE_GetRoot( int drive )
367 if (!DRIVE_IsValid( drive )) return NULL;
368 return DOSDrives[drive].root;
372 /***********************************************************************
373 * DRIVE_GetDosCwd
375 const char * DRIVE_GetDosCwd( int drive )
377 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
378 if (!DRIVE_IsValid( drive )) return NULL;
380 /* Check if we need to change the directory to the new task. */
381 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
382 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
383 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
385 /* Perform the task-switch */
386 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
387 DRIVE_LastTask = GetCurrentTask();
389 return DOSDrives[drive].dos_cwd;
393 /***********************************************************************
394 * DRIVE_GetUnixCwd
396 const char * DRIVE_GetUnixCwd( int drive )
398 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
399 if (!DRIVE_IsValid( drive )) return NULL;
401 /* Check if we need to change the directory to the new task. */
402 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
403 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
404 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
406 /* Perform the task-switch */
407 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
408 DRIVE_LastTask = GetCurrentTask();
410 return DOSDrives[drive].unix_cwd;
414 /***********************************************************************
415 * DRIVE_GetDevice
417 const char * DRIVE_GetDevice( int drive )
419 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
423 /***********************************************************************
424 * DRIVE_ReadSuperblock
426 * NOTE
427 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
428 * to check, that they are writing on a FAT filesystem !
430 int DRIVE_ReadSuperblock (int drive, char * buff)
432 #define DRIVE_SUPER 96
433 int fd;
434 off_t offs;
436 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
437 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
439 struct stat st;
440 if (!DOSDrives[drive].device)
441 ERR("No device configured for drive %c: !\n", 'A'+drive);
442 else
443 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
444 (stat(DOSDrives[drive].device, &st)) ?
445 "not available or symlink not valid ?" : "no permission");
446 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
447 PROFILE_UsageWineIni();
448 return -1;
451 switch(DOSDrives[drive].type)
453 case TYPE_FLOPPY:
454 case TYPE_HD:
455 offs = 0;
456 break;
457 case TYPE_CDROM:
458 /* FIXME: Maybe we should search for the first data track on the CD,
459 not just assume that it is the first track */
460 offs = (off_t)2048*(16+0);
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 WINE_CDAUDIO wcda;
548 if (!(CDROM_Open(&wcda, drive)))
550 int media = CDROM_GetMediaType(&wcda);
552 if (media == CDS_AUDIO)
554 strcpy(DOSDrives[drive].label_read, "Audio CD ");
555 read = 1;
557 else
558 if (media == CDS_NO_INFO)
560 strcpy(DOSDrives[drive].label_read, " ");
561 read = 1;
564 CDROM_Close(&wcda);
567 if ((!read) && (DOSDrives[drive].read_volinfo))
569 if (DRIVE_ReadSuperblock(drive,(char *) buff))
570 ERR("Invalid or unreadable superblock on %s (%c:).\n",
571 DOSDrives[drive].device, (char)(drive+'A'));
572 else {
573 if (DOSDrives[drive].type == TYPE_CDROM)
574 offs = 40;
575 else
576 if (DOSDrives[drive].type == TYPE_FLOPPY ||
577 DOSDrives[drive].type == TYPE_HD)
578 offs = 0x2b;
580 /* FIXME: ISO9660 uses 32-bytes long label. Should we do also? */
581 if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
582 DOSDrives[drive].label_read[11]='\0';
583 read = 1;
587 return (read) ?
588 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
592 /***********************************************************************
593 * DRIVE_GetSerialNumber
595 DWORD DRIVE_GetSerialNumber( int drive )
597 DWORD serial = 0;
598 char buff[DRIVE_SUPER];
600 if (!DRIVE_IsValid( drive )) return 0;
602 if (DOSDrives[drive].read_volinfo)
604 switch(DOSDrives[drive].type)
606 case TYPE_FLOPPY:
607 case TYPE_HD:
608 if (DRIVE_ReadSuperblock(drive,(char *) buff))
609 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
610 " Maybe not FAT?\n" ,
611 DOSDrives[drive].device, 'A'+drive);
612 else
613 serial = *((DWORD*)(buff+0x27));
614 break;
615 case TYPE_CDROM:
616 serial = CDROM_GetSerial(drive);
617 break;
618 default:
619 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
623 return (serial) ? serial : DOSDrives[drive].serial_conf;
627 /***********************************************************************
628 * DRIVE_SetSerialNumber
630 int DRIVE_SetSerialNumber( int drive, DWORD serial )
632 char buff[DRIVE_SUPER];
634 if (!DRIVE_IsValid( drive )) return 0;
636 if (DOSDrives[drive].read_volinfo)
638 if ((DOSDrives[drive].type != TYPE_FLOPPY) &&
639 (DOSDrives[drive].type != TYPE_HD)) return 0;
640 /* check, if the drive has a FAT filesystem */
641 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
642 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
643 return 1;
646 if (DOSDrives[drive].type == TYPE_CDROM) return 0;
647 DOSDrives[drive].serial_conf = serial;
648 return 1;
652 /***********************************************************************
653 * DRIVE_GetType
655 DRIVETYPE DRIVE_GetType( int drive )
657 if (!DRIVE_IsValid( drive )) return TYPE_INVALID;
658 return DOSDrives[drive].type;
662 /***********************************************************************
663 * DRIVE_GetFlags
665 UINT DRIVE_GetFlags( int drive )
667 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
668 return DOSDrives[drive].flags;
672 /***********************************************************************
673 * DRIVE_Chdir
675 int DRIVE_Chdir( int drive, const char *path )
677 DOS_FULL_NAME full_name;
678 char buffer[MAX_PATHNAME_LEN];
679 LPSTR unix_cwd;
680 BY_HANDLE_FILE_INFORMATION info;
681 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
683 strcpy( buffer, "A:" );
684 buffer[0] += drive;
685 TRACE("(%c:,%s)\n", buffer[0], path );
686 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
688 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
689 if (!FILE_Stat( full_name.long_name, &info )) return 0;
690 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
692 SetLastError( ERROR_FILE_NOT_FOUND );
693 return 0;
695 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
696 while (*unix_cwd == '/') unix_cwd++;
698 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
699 'A' + drive, unix_cwd, full_name.short_name + 3 );
701 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
702 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
703 DOSDrives[drive].dos_cwd = HEAP_strdupA( GetProcessHeap(), 0,
704 full_name.short_name + 3 );
705 DOSDrives[drive].unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, unix_cwd );
707 if (pTask && (pTask->curdrive & 0x80) &&
708 ((pTask->curdrive & ~0x80) == drive))
710 lstrcpynA( pTask->curdir, full_name.short_name + 2,
711 sizeof(pTask->curdir) );
712 DRIVE_LastTask = GetCurrentTask();
714 return 1;
718 /***********************************************************************
719 * DRIVE_Disable
721 int DRIVE_Disable( int drive )
723 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
725 SetLastError( ERROR_INVALID_DRIVE );
726 return 0;
728 DOSDrives[drive].flags |= DRIVE_DISABLED;
729 return 1;
733 /***********************************************************************
734 * DRIVE_Enable
736 int DRIVE_Enable( int drive )
738 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
740 SetLastError( ERROR_INVALID_DRIVE );
741 return 0;
743 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
744 return 1;
748 /***********************************************************************
749 * DRIVE_SetLogicalMapping
751 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
753 /* If new_drive is already valid, do nothing and return 0
754 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
756 DOSDRIVE *old, *new;
758 old = DOSDrives + existing_drive;
759 new = DOSDrives + new_drive;
761 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
762 !old->root ||
763 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
765 SetLastError( ERROR_INVALID_DRIVE );
766 return 0;
769 if ( new->root )
771 TRACE("Can\'t map drive %c to drive %c - drive %c already exists\n",
772 'A' + existing_drive, 'A' + new_drive, 'A' + new_drive );
773 /* it is already mapped there, so return success */
774 if (!strcmp(old->root,new->root))
775 return 1;
776 return 0;
779 new->root = HEAP_strdupA( GetProcessHeap(), 0, old->root );
780 new->dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->dos_cwd );
781 new->unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->unix_cwd );
782 memcpy ( new->label_conf, old->label_conf, 12 );
783 new->serial_conf = old->serial_conf;
784 new->type = old->type;
785 new->flags = old->flags;
786 new->dev = old->dev;
787 new->ino = old->ino;
789 TRACE("Drive %c is now equal to drive %c\n",
790 'A' + new_drive, 'A' + existing_drive );
792 return 1;
796 /***********************************************************************
797 * DRIVE_OpenDevice
799 * Open the drive raw device and return a Unix fd (or -1 on error).
801 int DRIVE_OpenDevice( int drive, int flags )
803 if (!DRIVE_IsValid( drive )) return -1;
804 return open( DOSDrives[drive].device, flags );
808 /***********************************************************************
809 * DRIVE_RawRead
811 * Read raw sectors from a device
813 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
815 int fd;
817 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
819 lseek( fd, begin * 512, SEEK_SET );
820 /* FIXME: check errors */
821 read( fd, dataptr, nr_sect * 512 );
822 close( fd );
824 else
826 memset(dataptr, 0, nr_sect * 512);
827 if (fake_success)
829 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
830 if (begin == 1) *dataptr = 0xf8;
832 else
833 return 0;
835 return 1;
839 /***********************************************************************
840 * DRIVE_RawWrite
842 * Write raw sectors to a device
844 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
846 int fd;
848 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
850 lseek( fd, begin * 512, SEEK_SET );
851 /* FIXME: check errors */
852 write( fd, dataptr, nr_sect * 512 );
853 close( fd );
855 else
856 if (!(fake_success))
857 return 0;
859 return 1;
863 /***********************************************************************
864 * DRIVE_GetFreeSpace
866 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
867 PULARGE_INTEGER available )
869 struct statfs info;
870 unsigned long long bigsize,bigavail=0;
872 if (!DRIVE_IsValid(drive))
874 SetLastError( ERROR_INVALID_DRIVE );
875 return 0;
878 /* FIXME: add autoconf check for this */
879 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
880 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
881 #else
882 if (statfs( DOSDrives[drive].root, &info) < 0)
883 #endif
885 FILE_SetDosError();
886 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
887 return 0;
890 bigsize = (unsigned long long)info.f_bsize
891 * (unsigned long long)info.f_blocks;
892 #ifdef STATFS_HAS_BAVAIL
893 bigavail = (unsigned long long)info.f_bavail
894 * (unsigned long long)info.f_bsize;
895 #else
896 # ifdef STATFS_HAS_BFREE
897 bigavail = (unsigned long long)info.f_bfree
898 * (unsigned long long)info.f_bsize;
899 # else
900 # error "statfs has no bfree/bavail member!"
901 # endif
902 #endif
903 size->s.LowPart = (DWORD)bigsize;
904 size->s.HighPart = (DWORD)(bigsize>>32);
905 available->s.LowPart = (DWORD)bigavail;
906 available->s.HighPart = (DWORD)(bigavail>>32);
907 return 1;
910 /***********************************************************************
911 * DRIVE_GetCurrentDirectory
912 * Returns "X:\\path\\etc\\".
914 * Despite the API description, return required length including the
915 * terminating null when buffer too small. This is the real behaviour.
918 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
920 UINT ret;
921 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
923 assert(s);
924 ret = strlen(s) + 3; /* length of WHOLE current directory */
925 if (ret >= buflen) return ret + 1;
926 lstrcpynA( buf, "A:\\", MIN( 4, buflen ) );
927 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
928 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
929 return ret;
932 /***********************************************************************
933 * GetDiskFreeSpace16 (KERNEL.422)
935 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
936 LPDWORD sector_bytes, LPDWORD free_clusters,
937 LPDWORD total_clusters )
939 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
940 free_clusters, total_clusters );
944 /***********************************************************************
945 * GetDiskFreeSpace32A (KERNEL32.206)
947 * Fails if expression resulting from current drive's dir and "root"
948 * is not a root dir of the target drive.
950 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
951 * if the corresponding info is unneeded.
953 * FIXME: needs to support UNC names from Win95 OSR2 on.
955 * Behaviour under Win95a:
956 * CurrDir root result
957 * "E:\\TEST" "E:" FALSE
958 * "E:\\" "E:" TRUE
959 * "E:\\" "E" FALSE
960 * "E:\\" "\\" TRUE
961 * "E:\\TEST" "\\" TRUE
962 * "E:\\TEST" ":\\" FALSE
963 * "E:\\TEST" "E:\\" TRUE
964 * "E:\\TEST" "" FALSE
965 * "E:\\" "" FALSE (!)
966 * "E:\\" 0x0 TRUE
967 * "E:\\TEST" 0x0 TRUE (!)
968 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
969 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
971 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
972 LPDWORD sector_bytes, LPDWORD free_clusters,
973 LPDWORD total_clusters )
975 int drive;
976 ULARGE_INTEGER size,available;
977 LPCSTR path;
978 DWORD cluster_sec;
980 if ((!root) || (strcmp(root,"\\") == 0))
981 drive = DRIVE_GetCurrentDrive();
982 else
983 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
985 drive = toupper(root[0]) - 'A';
986 path = &root[2];
987 if (path[0] == '\0')
988 path = DRIVE_GetDosCwd(drive);
989 else
990 if (path[0] == '\\')
991 path++;
992 if (strlen(path)) /* oops, we are in a subdir */
993 return FALSE;
995 else
996 return FALSE;
998 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1000 /* Cap the size and available at 2GB as per specs. */
1001 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1003 size.s.HighPart = 0;
1004 size.s.LowPart = 0x7fffffff;
1006 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1008 available.s.HighPart =0;
1009 available.s.LowPart = 0x7fffffff;
1011 if (DRIVE_GetType(drive)==TYPE_CDROM) {
1012 if (sector_bytes)
1013 *sector_bytes = 2048;
1014 size.s.LowPart /= 2048;
1015 available.s.LowPart /= 2048;
1016 } else {
1017 if (sector_bytes)
1018 *sector_bytes = 512;
1019 size.s.LowPart /= 512;
1020 available.s.LowPart /= 512;
1022 /* fixme: probably have to adjust those variables too for CDFS */
1023 cluster_sec = 1;
1024 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1026 if (cluster_sectors)
1027 *cluster_sectors = cluster_sec;
1028 if (free_clusters)
1029 *free_clusters = available.s.LowPart / cluster_sec;
1030 if (total_clusters)
1031 *total_clusters = size.s.LowPart / cluster_sec;
1032 return TRUE;
1036 /***********************************************************************
1037 * GetDiskFreeSpace32W (KERNEL32.207)
1039 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1040 LPDWORD sector_bytes, LPDWORD free_clusters,
1041 LPDWORD total_clusters )
1043 LPSTR xroot;
1044 BOOL ret;
1046 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1047 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
1048 free_clusters, total_clusters );
1049 HeapFree( GetProcessHeap(), 0, xroot );
1050 return ret;
1054 /***********************************************************************
1055 * GetDiskFreeSpaceEx32A (KERNEL32.871)
1057 * This function is used to aquire the size of the available and
1058 * total space on a logical volume.
1060 * RETURNS
1062 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1063 * detailed error information.
1066 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1067 PULARGE_INTEGER avail,
1068 PULARGE_INTEGER total,
1069 PULARGE_INTEGER totalfree)
1071 int drive;
1072 ULARGE_INTEGER size,available;
1074 if (!root) drive = DRIVE_GetCurrentDrive();
1075 else
1077 if ((root[1]) && ((root[1] != ':') || (root[2] != '\\')))
1079 FIXME("there are valid root names which are not supported yet\n");
1080 /* ..like UNC names, for instance. */
1082 WARN("invalid root '%s'\n", root );
1083 return FALSE;
1085 drive = toupper(root[0]) - 'A';
1088 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1090 if (total)
1092 total->s.HighPart = size.s.HighPart;
1093 total->s.LowPart = size.s.LowPart ;
1096 if (totalfree)
1098 totalfree->s.HighPart = available.s.HighPart;
1099 totalfree->s.LowPart = available.s.LowPart ;
1102 if (avail)
1104 if (FIXME_ON(dosfs))
1106 /* On Windows2000, we need to check the disk quota
1107 allocated for the user owning the calling process. We
1108 don't want to be more obtrusive than necessary with the
1109 FIXME messages, so don't print the FIXME unless Wine is
1110 actually masquerading as Windows2000. */
1112 OSVERSIONINFOA ovi;
1113 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1114 if (GetVersionExA(&ovi))
1116 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1117 FIXME("no per-user quota support yet\n");
1121 /* Quick hack, should eventually be fixed to work 100% with
1122 Windows2000 (see comment above). */
1123 avail->s.HighPart = available.s.HighPart;
1124 avail->s.LowPart = available.s.LowPart ;
1127 return TRUE;
1130 /***********************************************************************
1131 * GetDiskFreeSpaceEx32W (KERNEL32.873)
1133 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1134 PULARGE_INTEGER total,
1135 PULARGE_INTEGER totalfree)
1137 LPSTR xroot;
1138 BOOL ret;
1140 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1141 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1142 HeapFree( GetProcessHeap(), 0, xroot );
1143 return ret;
1146 /***********************************************************************
1147 * GetDriveType16 (KERNEL.136)
1148 * This function returns the type of a drive in Win16.
1149 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1150 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1151 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1152 * do any pseudo-clever changes.
1154 * RETURNS
1155 * drivetype DRIVE_xxx
1157 UINT16 WINAPI GetDriveType16(
1158 UINT16 drive /* [in] number (NOT letter) of drive */
1160 TRACE("(%c:)\n", 'A' + drive );
1161 switch(DRIVE_GetType(drive))
1163 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
1164 case TYPE_HD: return DRIVE_FIXED;
1165 case TYPE_CDROM: return DRIVE_REMOTE;
1166 case TYPE_NETWORK: return DRIVE_REMOTE;
1167 case TYPE_INVALID:
1168 default: return DRIVE_CANNOTDETERMINE;
1173 /***********************************************************************
1174 * GetDriveType32A (KERNEL32.208)
1176 * Returns the type of the disk drive specified. If root is NULL the
1177 * root of the current directory is used.
1179 * RETURNS
1181 * Type of drive (from Win32 SDK):
1183 * DRIVE_UNKNOWN unable to find out anything about the drive
1184 * DRIVE_NO_ROOT_DIR nonexistand root dir
1185 * DRIVE_REMOVABLE the disk can be removed from the machine
1186 * DRIVE_FIXED the disk can not be removed from the machine
1187 * DRIVE_REMOTE network disk
1188 * DRIVE_CDROM CDROM drive
1189 * DRIVE_RAMDISK virtual disk in ram
1191 * DRIVE_DOESNOTEXIST XXX Not valid return value
1192 * DRIVE_CANNOTDETERMINE XXX Not valid return value
1194 * BUGS
1196 * Currently returns DRIVE_DOESNOTEXIST and DRIVE_CANNOTDETERMINE
1197 * when it really should return DRIVE_NO_ROOT_DIR and DRIVE_UNKNOWN.
1198 * Why were the former defines used?
1200 * DRIVE_RAMDISK is unsupported.
1202 UINT WINAPI GetDriveTypeA(LPCSTR root /* String describing drive */)
1204 int drive;
1205 TRACE("(%s)\n", debugstr_a(root));
1207 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1208 else
1210 if ((root[1]) && (root[1] != ':'))
1212 WARN("invalid root '%s'\n", debugstr_a(root));
1213 return DRIVE_DOESNOTEXIST;
1215 drive = toupper(root[0]) - 'A';
1217 switch(DRIVE_GetType(drive))
1219 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
1220 case TYPE_HD: return DRIVE_FIXED;
1221 case TYPE_CDROM: return DRIVE_CDROM;
1222 case TYPE_NETWORK: return DRIVE_REMOTE;
1223 case TYPE_INVALID: return DRIVE_DOESNOTEXIST;
1224 default: return DRIVE_CANNOTDETERMINE;
1229 /***********************************************************************
1230 * GetDriveType32W (KERNEL32.209)
1232 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1234 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1235 UINT ret = GetDriveTypeA( xpath );
1236 HeapFree( GetProcessHeap(), 0, xpath );
1237 return ret;
1241 /***********************************************************************
1242 * GetCurrentDirectory16 (KERNEL.411)
1244 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1246 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1250 /***********************************************************************
1251 * GetCurrentDirectory32A (KERNEL32.196)
1253 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1255 UINT ret;
1256 char longname[MAX_PATHNAME_LEN];
1257 char shortname[MAX_PATHNAME_LEN];
1258 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1259 if ( ret > MAX_PATHNAME_LEN ) {
1260 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1261 return ret;
1263 GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1264 ret = lstrlenA( longname ) + 1;
1265 if (ret > buflen) return ret;
1266 lstrcpyA(buf, longname);
1267 return ret - 1;
1270 /***********************************************************************
1271 * GetCurrentDirectory32W (KERNEL32.197)
1273 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1275 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1276 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1277 if (ret < buflen) lstrcpyAtoW ( buf, xpath );
1278 HeapFree( GetProcessHeap(), 0, xpath );
1279 return ret;
1283 /***********************************************************************
1284 * SetCurrentDirectory (KERNEL.412)
1286 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1288 return SetCurrentDirectoryA( dir );
1292 /***********************************************************************
1293 * SetCurrentDirectory32A (KERNEL32.479)
1295 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1297 int olddrive, drive = DRIVE_GetCurrentDrive();
1299 if (!dir) {
1300 ERR_(file)("(NULL)!\n");
1301 return FALSE;
1303 if (dir[0] && (dir[1]==':'))
1305 drive = tolower( *dir ) - 'a';
1306 dir += 2;
1309 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1310 sets pTask->curdir only if pTask->curdrive is drive */
1311 olddrive = drive; /* in case DRIVE_Chdir fails */
1312 if (!(DRIVE_SetCurrentDrive( drive )))
1313 return FALSE;
1314 /* FIXME: what about empty strings? Add a \\ ? */
1315 if (!DRIVE_Chdir( drive, dir )) {
1316 DRIVE_SetCurrentDrive(olddrive);
1317 return FALSE;
1319 return TRUE;
1323 /***********************************************************************
1324 * SetCurrentDirectory32W (KERNEL32.480)
1326 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1328 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1329 BOOL res = SetCurrentDirectoryA( dir );
1330 HeapFree( GetProcessHeap(), 0, dir );
1331 return res;
1335 /***********************************************************************
1336 * GetLogicalDriveStrings32A (KERNEL32.231)
1338 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1340 int drive, count;
1342 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1343 if (DRIVE_IsValid(drive)) count++;
1344 if ((count * 4) + 1 <= len)
1346 LPSTR p = buffer;
1347 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1348 if (DRIVE_IsValid(drive))
1350 *p++ = 'a' + drive;
1351 *p++ = ':';
1352 *p++ = '\\';
1353 *p++ = '\0';
1355 *p = '\0';
1356 return count * 4;
1358 else
1359 return (count * 4) + 1;/* account for terminating null */
1360 /* The API tells about these different return values */
1364 /***********************************************************************
1365 * GetLogicalDriveStrings32W (KERNEL32.232)
1367 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1369 int drive, count;
1371 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1372 if (DRIVE_IsValid(drive)) count++;
1373 if (count * 4 * sizeof(WCHAR) <= len)
1375 LPWSTR p = buffer;
1376 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1377 if (DRIVE_IsValid(drive))
1379 *p++ = (WCHAR)('a' + drive);
1380 *p++ = (WCHAR)':';
1381 *p++ = (WCHAR)'\\';
1382 *p++ = (WCHAR)'\0';
1384 *p = (WCHAR)'\0';
1386 return count * 4 * sizeof(WCHAR);
1390 /***********************************************************************
1391 * GetLogicalDrives (KERNEL32.233)
1393 DWORD WINAPI GetLogicalDrives(void)
1395 DWORD ret = 0;
1396 int drive;
1398 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1400 if ( (DRIVE_IsValid(drive)) ||
1401 (DOSDrives[drive].type == TYPE_CDROM)) /* audio CD is also valid */
1402 ret |= (1 << drive);
1404 return ret;
1408 /***********************************************************************
1409 * GetVolumeInformation32A (KERNEL32.309)
1411 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1412 DWORD label_len, DWORD *serial,
1413 DWORD *filename_len, DWORD *flags,
1414 LPSTR fsname, DWORD fsname_len )
1416 int drive;
1417 char *cp;
1419 /* FIXME, SetLastError()s missing */
1421 if (!root) drive = DRIVE_GetCurrentDrive();
1422 else
1424 if ((root[1]) && (root[1] != ':'))
1426 WARN("invalid root '%s'\n",root);
1427 return FALSE;
1429 drive = toupper(root[0]) - 'A';
1431 if (!DRIVE_IsValid( drive )) return FALSE;
1432 if (label)
1434 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1435 for (cp = label; *cp; cp++);
1436 while (cp != label && *(cp-1) == ' ') cp--;
1437 *cp = '\0';
1439 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1441 /* Set the filesystem information */
1442 /* Note: we only emulate a FAT fs at present */
1444 if (filename_len) {
1445 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1446 *filename_len = 12;
1447 else
1448 *filename_len = 255;
1450 if (flags)
1452 *flags=0;
1453 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1454 *flags|=FS_CASE_SENSITIVE;
1455 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1456 *flags|=FS_CASE_IS_PRESERVED;
1458 if (fsname) {
1459 /* Diablo checks that return code ... */
1460 if (DRIVE_GetType(drive)==TYPE_CDROM)
1461 lstrcpynA( fsname, "CDFS", fsname_len );
1462 else
1463 lstrcpynA( fsname, "FAT", fsname_len );
1465 return TRUE;
1469 /***********************************************************************
1470 * GetVolumeInformation32W (KERNEL32.310)
1472 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1473 DWORD label_len, DWORD *serial,
1474 DWORD *filename_len, DWORD *flags,
1475 LPWSTR fsname, DWORD fsname_len )
1477 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1478 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1479 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1480 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1481 filename_len, flags, xfsname,
1482 fsname_len );
1483 if (ret)
1485 if (label) lstrcpyAtoW( label, xvolname );
1486 if (fsname) lstrcpyAtoW( fsname, xfsname );
1488 HeapFree( GetProcessHeap(), 0, xroot );
1489 HeapFree( GetProcessHeap(), 0, xvolname );
1490 HeapFree( GetProcessHeap(), 0, xfsname );
1491 return ret;
1494 /***********************************************************************
1495 * SetVolumeLabelA (KERNEL32.675)
1497 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1499 int drive;
1501 /* FIXME, SetLastErrors missing */
1503 if (!root) drive = DRIVE_GetCurrentDrive();
1504 else
1506 if ((root[1]) && (root[1] != ':'))
1508 WARN("invalid root '%s'\n",root);
1509 return FALSE;
1511 drive = toupper(root[0]) - 'A';
1513 if (!DRIVE_IsValid( drive )) return FALSE;
1515 /* some copy protection stuff check this */
1516 if (DRIVE_GetType( drive ) == TYPE_CDROM) return FALSE;
1518 FIXME("(%s,%s),stub!\n", root, volname);
1519 return TRUE;
1522 /***********************************************************************
1523 * SetVolumeLabelW (KERNEL32.676)
1525 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1527 LPSTR xroot, xvol;
1528 BOOL ret;
1530 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1531 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1532 ret = SetVolumeLabelA( xroot, xvol );
1533 HeapFree( GetProcessHeap(), 0, xroot );
1534 HeapFree( GetProcessHeap(), 0, xvol );
1535 return ret;