The DISPPARAMS parameter array is a reverse-order array.
[wine.git] / files / drive.c
blob315717072c9cc029f2dafc4e7742b63d18f3ecda
1 /*
2 * DOS drives handling functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
7 * Label & serial number read support.
8 * (c) 1999 Petr Tomasek <tomasek@etf.cuni.cz>
9 * (c) 2000 Andreas Mohr (changes)
13 #include "config.h"
15 #include <assert.h>
16 #include <ctype.h>
17 #include <string.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <unistd.h>
26 #ifdef HAVE_SYS_PARAM_H
27 # include <sys/param.h>
28 #endif
29 #ifdef STATFS_DEFINED_BY_SYS_VFS
30 # include <sys/vfs.h>
31 #else
32 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
33 # include <sys/mount.h>
34 # else
35 # ifdef STATFS_DEFINED_BY_SYS_STATFS
36 # include <sys/statfs.h>
37 # endif
38 # endif
39 #endif
41 #include "winbase.h"
42 #include "ntddk.h"
43 #include "wine/winbase16.h" /* for GetCurrentTask */
44 #include "winerror.h"
45 #include "drive.h"
46 #include "cdrom.h"
47 #include "file.h"
48 #include "heap.h"
49 #include "msdos.h"
50 #include "options.h"
51 #include "wine/port.h"
52 #include "task.h"
53 #include "debugtools.h"
54 #include "wine/server.h"
56 DEFAULT_DEBUG_CHANNEL(dosfs);
57 DECLARE_DEBUG_CHANNEL(file);
59 typedef struct
61 char *root; /* root dir in Unix format without trailing / */
62 char *dos_cwd; /* cwd in DOS format without leading or trailing \ */
63 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
64 char *device; /* raw device path */
65 char label_conf[12]; /* drive label as cfg'd in wine config */
66 char label_read[12]; /* drive label as read from device */
67 DWORD serial_conf; /* drive serial number as cfg'd in wine config */
68 UINT type; /* drive type */
69 UINT flags; /* drive flags */
70 dev_t dev; /* unix device number */
71 ino_t ino; /* unix inode number */
72 } DOSDRIVE;
75 static const char * const DRIVE_Types[] =
77 "", /* DRIVE_UNKNOWN */
78 "", /* DRIVE_NO_ROOT_DIR */
79 "floppy", /* DRIVE_REMOVABLE */
80 "hd", /* DRIVE_FIXED */
81 "network", /* DRIVE_REMOTE */
82 "cdrom", /* DRIVE_CDROM */
83 "ramdisk" /* DRIVE_RAMDISK */
87 /* Known filesystem types */
89 typedef struct
91 const char *name;
92 UINT flags;
93 } FS_DESCR;
95 static const FS_DESCR DRIVE_Filesystems[] =
97 { "unix", DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
98 { "msdos", DRIVE_SHORT_NAMES },
99 { "dos", DRIVE_SHORT_NAMES },
100 { "fat", DRIVE_SHORT_NAMES },
101 { "vfat", DRIVE_CASE_PRESERVING },
102 { "win95", DRIVE_CASE_PRESERVING },
103 { NULL, 0 }
107 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
108 static int DRIVE_CurDrive = -1;
110 static HTASK16 DRIVE_LastTask = 0;
112 /* strdup on the process heap */
113 inline static char *heap_strdup( const char *str )
115 INT len = strlen(str) + 1;
116 LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
117 if (p) memcpy( p, str, len );
118 return p;
121 /***********************************************************************
122 * DRIVE_GetDriveType
124 static UINT DRIVE_GetDriveType( const char *name )
126 char buffer[20];
127 int i;
129 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
130 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
132 if (!strcasecmp( buffer, DRIVE_Types[i] )) return i;
134 MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
135 name, buffer );
136 return DRIVE_FIXED;
140 /***********************************************************************
141 * DRIVE_GetFSFlags
143 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
145 const FS_DESCR *descr;
147 for (descr = DRIVE_Filesystems; descr->name; descr++)
148 if (!strcasecmp( value, descr->name )) return descr->flags;
149 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
150 name, value );
151 return DRIVE_CASE_PRESERVING;
155 /***********************************************************************
156 * DRIVE_Init
158 int DRIVE_Init(void)
160 int i, len, count = 0;
161 char name[] = "Drive A";
162 char drive_env[] = "=A:";
163 char path[MAX_PATHNAME_LEN];
164 char buffer[80];
165 struct stat drive_stat_buffer;
166 char *p;
167 DOSDRIVE *drive;
169 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
171 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
172 if (path[0])
174 p = path + strlen(path) - 1;
175 while ((p > path) && (*p == '/')) *p-- = '\0';
177 if (path[0] == '/')
179 drive->root = heap_strdup( path );
181 else
183 /* relative paths are relative to config dir */
184 const char *config = get_config_dir();
185 drive->root = HeapAlloc( GetProcessHeap(), 0, strlen(config) + strlen(path) + 2 );
186 sprintf( drive->root, "%s/%s", config, path );
189 if (stat( drive->root, &drive_stat_buffer ))
191 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
192 drive->root, strerror(errno), 'A' + i);
193 HeapFree( GetProcessHeap(), 0, drive->root );
194 drive->root = NULL;
195 continue;
197 if (!S_ISDIR(drive_stat_buffer.st_mode))
199 MESSAGE("%s is not a directory, ignoring drive %c:\n",
200 drive->root, 'A' + i );
201 HeapFree( GetProcessHeap(), 0, drive->root );
202 drive->root = NULL;
203 continue;
206 drive->dos_cwd = heap_strdup( "" );
207 drive->unix_cwd = heap_strdup( "" );
208 drive->type = DRIVE_GetDriveType( name );
209 drive->device = NULL;
210 drive->flags = 0;
211 drive->dev = drive_stat_buffer.st_dev;
212 drive->ino = drive_stat_buffer.st_ino;
214 /* Get the drive label */
215 PROFILE_GetWineIniString( name, "Label", "", drive->label_conf, 12 );
216 if ((len = strlen(drive->label_conf)) < 11)
218 /* Pad label with spaces */
219 memset( drive->label_conf + len, ' ', 11 - len );
220 drive->label_conf[11] = '\0';
223 /* Get the serial number */
224 PROFILE_GetWineIniString( name, "Serial", "12345678",
225 buffer, sizeof(buffer) );
226 drive->serial_conf = strtoul( buffer, NULL, 16 );
228 /* Get the filesystem type */
229 PROFILE_GetWineIniString( name, "Filesystem", "win95",
230 buffer, sizeof(buffer) );
231 drive->flags = DRIVE_GetFSFlags( name, buffer );
233 /* Get the device */
234 PROFILE_GetWineIniString( name, "Device", "",
235 buffer, sizeof(buffer) );
236 if (buffer[0])
238 drive->device = heap_strdup( buffer );
239 if (PROFILE_GetWineIniBool( name, "ReadVolInfo", 1))
240 drive->flags |= DRIVE_READ_VOL_INFO;
243 /* Get the FailReadOnly flag */
244 if (PROFILE_GetWineIniBool( name, "FailReadOnly", 0 ))
245 drive->flags |= DRIVE_FAIL_READ_ONLY;
247 /* Make the first hard disk the current drive */
248 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
249 DRIVE_CurDrive = i;
251 count++;
252 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
253 "flags=%08x dev=%x ino=%x\n",
254 name, drive->root, DRIVE_Types[drive->type],
255 drive->label_conf, drive->serial_conf, drive->flags,
256 (int)drive->dev, (int)drive->ino );
258 else WARN("%s: not defined\n", name );
261 if (!count)
263 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
264 /* Create a C drive pointing to Unix root dir */
265 DOSDrives[2].root = heap_strdup( "/" );
266 DOSDrives[2].dos_cwd = heap_strdup( "" );
267 DOSDrives[2].unix_cwd = heap_strdup( "" );
268 strcpy( DOSDrives[2].label_conf, "Drive C " );
269 DOSDrives[2].serial_conf = 12345678;
270 DOSDrives[2].type = DRIVE_FIXED;
271 DOSDrives[2].device = NULL;
272 DOSDrives[2].flags = 0;
273 DRIVE_CurDrive = 2;
276 /* Make sure the current drive is valid */
277 if (DRIVE_CurDrive == -1)
279 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
281 if (drive->root && !(drive->flags & DRIVE_DISABLED))
283 DRIVE_CurDrive = i;
284 break;
289 /* get current working directory info for all drives */
290 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
292 if (!GetEnvironmentVariableA(drive_env, path, sizeof(path))) continue;
293 /* sanity check */
294 if (toupper(path[0]) != drive_env[1] || path[1] != ':') continue;
295 DRIVE_Chdir( i, path + 2 );
297 return 1;
301 /***********************************************************************
302 * DRIVE_IsValid
304 int DRIVE_IsValid( int drive )
306 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
307 return (DOSDrives[drive].root &&
308 !(DOSDrives[drive].flags & DRIVE_DISABLED));
312 /***********************************************************************
313 * DRIVE_GetCurrentDrive
315 int DRIVE_GetCurrentDrive(void)
317 TDB *pTask = TASK_GetCurrent();
318 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
319 return DRIVE_CurDrive;
323 /***********************************************************************
324 * DRIVE_SetCurrentDrive
326 int DRIVE_SetCurrentDrive( int drive )
328 TDB *pTask = TASK_GetCurrent();
329 if (!DRIVE_IsValid( drive ))
331 SetLastError( ERROR_INVALID_DRIVE );
332 return 0;
334 TRACE("%c:\n", 'A' + drive );
335 DRIVE_CurDrive = drive;
336 if (pTask) pTask->curdrive = drive | 0x80;
337 chdir(DRIVE_GetUnixCwd(drive));
338 return 1;
342 /***********************************************************************
343 * DRIVE_FindDriveRoot
345 * Find a drive for which the root matches the beginning of the given path.
346 * This can be used to translate a Unix path into a drive + DOS path.
347 * Return value is the drive, or -1 on error. On success, path is modified
348 * to point to the beginning of the DOS path.
350 int DRIVE_FindDriveRoot( const char **path )
352 /* idea: check at all '/' positions.
353 * If the device and inode of that path is identical with the
354 * device and inode of the current drive then we found a solution.
355 * If there is another drive pointing to a deeper position in
356 * the file tree, we want to find that one, not the earlier solution.
358 int drive, rootdrive = -1;
359 char buffer[MAX_PATHNAME_LEN];
360 char *next = buffer;
361 const char *p = *path;
362 struct stat st;
364 strcpy( buffer, "/" );
365 for (;;)
367 if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
369 /* Find the drive */
371 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
373 if (!DOSDrives[drive].root ||
374 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
376 if ((DOSDrives[drive].dev == st.st_dev) &&
377 (DOSDrives[drive].ino == st.st_ino))
379 rootdrive = drive;
380 *path = p;
381 break;
385 /* Get the next path component */
387 *next++ = '/';
388 while ((*p == '/') || (*p == '\\')) p++;
389 if (!*p) break;
390 while (!IS_END_OF_NAME(*p)) *next++ = *p++;
391 *next = 0;
393 *next = 0;
395 if (rootdrive != -1)
396 TRACE("%s -> drive %c:, root='%s', name='%s'\n",
397 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, *path );
398 return rootdrive;
402 /***********************************************************************
403 * DRIVE_GetRoot
405 const char * DRIVE_GetRoot( int drive )
407 if (!DRIVE_IsValid( drive )) return NULL;
408 return DOSDrives[drive].root;
412 /***********************************************************************
413 * DRIVE_GetDosCwd
415 const char * DRIVE_GetDosCwd( int drive )
417 TDB *pTask = TASK_GetCurrent();
418 if (!DRIVE_IsValid( drive )) return NULL;
420 /* Check if we need to change the directory to the new task. */
421 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
422 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
423 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
425 /* Perform the task-switch */
426 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
427 DRIVE_LastTask = GetCurrentTask();
429 return DOSDrives[drive].dos_cwd;
433 /***********************************************************************
434 * DRIVE_GetUnixCwd
436 const char * DRIVE_GetUnixCwd( int drive )
438 TDB *pTask = TASK_GetCurrent();
439 if (!DRIVE_IsValid( drive )) return NULL;
441 /* Check if we need to change the directory to the new task. */
442 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
443 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
444 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
446 /* Perform the task-switch */
447 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
448 DRIVE_LastTask = GetCurrentTask();
450 return DOSDrives[drive].unix_cwd;
454 /***********************************************************************
455 * DRIVE_GetDevice
457 const char * DRIVE_GetDevice( int drive )
459 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
463 /***********************************************************************
464 * DRIVE_ReadSuperblock
466 * NOTE
467 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
468 * to check, that they are writing on a FAT filesystem !
470 int DRIVE_ReadSuperblock (int drive, char * buff)
472 #define DRIVE_SUPER 96
473 int fd;
474 off_t offs;
476 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
477 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
479 struct stat st;
480 if (!DOSDrives[drive].device)
481 ERR("No device configured for drive %c: !\n", 'A'+drive);
482 else
483 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
484 (stat(DOSDrives[drive].device, &st)) ?
485 "not available or symlink not valid ?" : "no permission");
486 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
487 PROFILE_UsageWineIni();
488 return -1;
491 switch(DOSDrives[drive].type)
493 case DRIVE_REMOVABLE:
494 case DRIVE_FIXED:
495 offs = 0;
496 break;
497 case DRIVE_CDROM:
498 offs = CDROM_Data_FindBestVoldesc(fd);
499 break;
500 default:
501 offs = 0;
502 break;
505 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs)) return -4;
506 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER) return -2;
508 switch(DOSDrives[drive].type)
510 case DRIVE_REMOVABLE:
511 case DRIVE_FIXED:
512 if ((buff[0x26]!=0x29) || /* Check for FAT present */
513 /* FIXME: do really all FAT have their name beginning with
514 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
515 memcmp( buff+0x36,"FAT",3))
517 ERR("The filesystem is not FAT !! (device=%s)\n",
518 DOSDrives[drive].device);
519 return -3;
521 break;
522 case DRIVE_CDROM:
523 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
524 return -3;
525 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
526 break;
527 default:
528 return -3;
529 break;
532 return close(fd);
536 /***********************************************************************
537 * DRIVE_WriteSuperblockEntry
539 * NOTE
540 * We are writing as little as possible (ie. not the whole SuperBlock)
541 * not to interfere with kernel. The drive can be mounted !
543 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
545 int fd;
547 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
549 ERR("Cannot open the device %s (for writing)\n",
550 DOSDrives[drive].device);
551 return -1;
553 if (lseek(fd,ofs,SEEK_SET)!=ofs)
555 ERR("lseek failed on device %s !\n",
556 DOSDrives[drive].device);
557 close(fd);
558 return -2;
560 if (write(fd,buff,len)!=len)
562 close(fd);
563 ERR("Cannot write on %s !\n",
564 DOSDrives[drive].device);
565 return -3;
567 return close (fd);
572 /***********************************************************************
573 * DRIVE_GetLabel
575 const char * DRIVE_GetLabel( int drive )
577 int read = 0;
578 char buff[DRIVE_SUPER];
579 int offs = -1;
581 if (!DRIVE_IsValid( drive )) return NULL;
582 if (DOSDrives[drive].type == DRIVE_CDROM)
584 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
586 else
587 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
589 if (DRIVE_ReadSuperblock(drive,(char *) buff))
590 ERR("Invalid or unreadable superblock on %s (%c:).\n",
591 DOSDrives[drive].device, (char)(drive+'A'));
592 else {
593 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
594 DOSDrives[drive].type == DRIVE_FIXED)
595 offs = 0x2b;
597 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
598 if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
599 DOSDrives[drive].label_read[11]='\0';
600 read = 1;
604 return (read) ?
605 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
609 /***********************************************************************
610 * DRIVE_GetSerialNumber
612 DWORD DRIVE_GetSerialNumber( int drive )
614 DWORD serial = 0;
615 char buff[DRIVE_SUPER];
617 if (!DRIVE_IsValid( drive )) return 0;
619 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
621 switch(DOSDrives[drive].type)
623 case DRIVE_REMOVABLE:
624 case DRIVE_FIXED:
625 if (DRIVE_ReadSuperblock(drive,(char *) buff))
626 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
627 " Maybe not FAT?\n" ,
628 DOSDrives[drive].device, 'A'+drive);
629 else
630 serial = *((DWORD*)(buff+0x27));
631 break;
632 case DRIVE_CDROM:
633 serial = CDROM_GetSerial(drive);
634 break;
635 default:
636 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
640 return (serial) ? serial : DOSDrives[drive].serial_conf;
644 /***********************************************************************
645 * DRIVE_SetSerialNumber
647 int DRIVE_SetSerialNumber( int drive, DWORD serial )
649 char buff[DRIVE_SUPER];
651 if (!DRIVE_IsValid( drive )) return 0;
653 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
655 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
656 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
657 /* check, if the drive has a FAT filesystem */
658 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
659 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
660 return 1;
663 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
664 DOSDrives[drive].serial_conf = serial;
665 return 1;
669 /***********************************************************************
670 * DRIVE_GetType
672 static UINT DRIVE_GetType( int drive )
674 if (!DRIVE_IsValid( drive )) return DRIVE_UNKNOWN;
675 return DOSDrives[drive].type;
679 /***********************************************************************
680 * DRIVE_GetFlags
682 UINT DRIVE_GetFlags( int drive )
684 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
685 return DOSDrives[drive].flags;
689 /***********************************************************************
690 * DRIVE_Chdir
692 int DRIVE_Chdir( int drive, const char *path )
694 DOS_FULL_NAME full_name;
695 char buffer[MAX_PATHNAME_LEN];
696 LPSTR unix_cwd;
697 BY_HANDLE_FILE_INFORMATION info;
698 TDB *pTask = TASK_GetCurrent();
700 strcpy( buffer, "A:" );
701 buffer[0] += drive;
702 TRACE("(%s,%s)\n", buffer, path );
703 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
705 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
706 if (!FILE_Stat( full_name.long_name, &info )) return 0;
707 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
709 SetLastError( ERROR_FILE_NOT_FOUND );
710 return 0;
712 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
713 while (*unix_cwd == '/') unix_cwd++;
715 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
716 'A' + drive, unix_cwd, full_name.short_name + 3 );
718 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
719 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
720 DOSDrives[drive].dos_cwd = heap_strdup( full_name.short_name + 3 );
721 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
723 if (pTask && (pTask->curdrive & 0x80) &&
724 ((pTask->curdrive & ~0x80) == drive))
726 lstrcpynA( pTask->curdir, full_name.short_name + 2,
727 sizeof(pTask->curdir) );
728 DRIVE_LastTask = GetCurrentTask();
729 chdir(unix_cwd); /* Only change if on current drive */
731 return 1;
735 /***********************************************************************
736 * DRIVE_Disable
738 int DRIVE_Disable( int drive )
740 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
742 SetLastError( ERROR_INVALID_DRIVE );
743 return 0;
745 DOSDrives[drive].flags |= DRIVE_DISABLED;
746 return 1;
750 /***********************************************************************
751 * DRIVE_Enable
753 int DRIVE_Enable( int drive )
755 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
757 SetLastError( ERROR_INVALID_DRIVE );
758 return 0;
760 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
761 return 1;
765 /***********************************************************************
766 * DRIVE_SetLogicalMapping
768 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
770 /* If new_drive is already valid, do nothing and return 0
771 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
773 DOSDRIVE *old, *new;
775 old = DOSDrives + existing_drive;
776 new = DOSDrives + new_drive;
778 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
779 !old->root ||
780 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
782 SetLastError( ERROR_INVALID_DRIVE );
783 return 0;
786 if ( new->root )
788 TRACE("Can't map drive %c: to already existing drive %c:\n",
789 'A' + existing_drive, 'A' + new_drive );
790 /* it is already mapped there, so return success */
791 if (!strcmp(old->root,new->root))
792 return 1;
793 return 0;
796 new->root = heap_strdup( old->root );
797 new->dos_cwd = heap_strdup( old->dos_cwd );
798 new->unix_cwd = heap_strdup( old->unix_cwd );
799 new->device = heap_strdup( old->device );
800 memcpy ( new->label_conf, old->label_conf, 12 );
801 memcpy ( new->label_read, old->label_read, 12 );
802 new->serial_conf = old->serial_conf;
803 new->type = old->type;
804 new->flags = old->flags;
805 new->dev = old->dev;
806 new->ino = old->ino;
808 TRACE("Drive %c: is now equal to drive %c:\n",
809 'A' + new_drive, 'A' + existing_drive );
811 return 1;
815 /***********************************************************************
816 * DRIVE_OpenDevice
818 * Open the drive raw device and return a Unix fd (or -1 on error).
820 int DRIVE_OpenDevice( int drive, int flags )
822 if (!DRIVE_IsValid( drive )) return -1;
823 return open( DOSDrives[drive].device, flags );
827 /***********************************************************************
828 * DRIVE_RawRead
830 * Read raw sectors from a device
832 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
834 int fd;
836 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
838 lseek( fd, begin * 512, SEEK_SET );
839 /* FIXME: check errors */
840 read( fd, dataptr, nr_sect * 512 );
841 close( fd );
843 else
845 memset(dataptr, 0, nr_sect * 512);
846 if (fake_success)
848 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
849 if (begin == 1) *dataptr = 0xf8;
851 else
852 return 0;
854 return 1;
858 /***********************************************************************
859 * DRIVE_RawWrite
861 * Write raw sectors to a device
863 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
865 int fd;
867 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
869 lseek( fd, begin * 512, SEEK_SET );
870 /* FIXME: check errors */
871 write( fd, dataptr, nr_sect * 512 );
872 close( fd );
874 else
875 if (!(fake_success))
876 return 0;
878 return 1;
882 /***********************************************************************
883 * DRIVE_GetFreeSpace
885 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
886 PULARGE_INTEGER available )
888 struct statfs info;
890 if (!DRIVE_IsValid(drive))
892 SetLastError( ERROR_INVALID_DRIVE );
893 return 0;
896 /* FIXME: add autoconf check for this */
897 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
898 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
899 #else
900 if (statfs( DOSDrives[drive].root, &info) < 0)
901 #endif
903 FILE_SetDosError();
904 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
905 return 0;
908 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
909 #ifdef STATFS_HAS_BAVAIL
910 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
911 #else
912 # ifdef STATFS_HAS_BFREE
913 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
914 # else
915 # error "statfs has no bfree/bavail member!"
916 # endif
917 #endif
918 if (DOSDrives[drive].type == DRIVE_CDROM)
919 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
920 available->QuadPart = 0;
922 return 1;
925 /***********************************************************************
926 * DRIVE_GetCurrentDirectory
927 * Returns "X:\\path\\etc\\".
929 * Despite the API description, return required length including the
930 * terminating null when buffer too small. This is the real behaviour.
933 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
935 UINT ret;
936 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
938 assert(s);
939 ret = strlen(s) + 3; /* length of WHOLE current directory */
940 if (ret >= buflen) return ret + 1;
941 lstrcpynA( buf, "A:\\", min( 4, buflen ) );
942 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
943 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
944 return ret;
948 /***********************************************************************
949 * DRIVE_BuildEnv
951 * Build the environment array containing the drives' current directories.
952 * Resulting pointer must be freed with HeapFree.
954 char *DRIVE_BuildEnv(void)
956 int i, length = 0;
957 const char *cwd[MAX_DOS_DRIVES];
958 char *env, *p;
960 for (i = 0; i < MAX_DOS_DRIVES; i++)
962 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0]) length += strlen(cwd[i]) + 8;
964 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
965 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
967 if (cwd[i] && cwd[i][0])
968 p += sprintf( p, "=%c:=%c:\\%s", 'A'+i, 'A'+i, cwd[i] ) + 1;
970 *p = 0;
971 return env;
975 /***********************************************************************
976 * GetDiskFreeSpace (KERNEL.422)
978 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
979 LPDWORD sector_bytes, LPDWORD free_clusters,
980 LPDWORD total_clusters )
982 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
983 free_clusters, total_clusters );
987 /***********************************************************************
988 * GetDiskFreeSpaceA (KERNEL32.@)
990 * Fails if expression resulting from current drive's dir and "root"
991 * is not a root dir of the target drive.
993 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
994 * if the corresponding info is unneeded.
996 * FIXME: needs to support UNC names from Win95 OSR2 on.
998 * Behaviour under Win95a:
999 * CurrDir root result
1000 * "E:\\TEST" "E:" FALSE
1001 * "E:\\" "E:" TRUE
1002 * "E:\\" "E" FALSE
1003 * "E:\\" "\\" TRUE
1004 * "E:\\TEST" "\\" TRUE
1005 * "E:\\TEST" ":\\" FALSE
1006 * "E:\\TEST" "E:\\" TRUE
1007 * "E:\\TEST" "" FALSE
1008 * "E:\\" "" FALSE (!)
1009 * "E:\\" 0x0 TRUE
1010 * "E:\\TEST" 0x0 TRUE (!)
1011 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1012 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1014 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1015 LPDWORD sector_bytes, LPDWORD free_clusters,
1016 LPDWORD total_clusters )
1018 int drive, sec_size;
1019 ULARGE_INTEGER size,available;
1020 LPCSTR path;
1021 DWORD cluster_sec;
1023 if ((!root) || (strcmp(root,"\\") == 0))
1024 drive = DRIVE_GetCurrentDrive();
1025 else
1026 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
1028 drive = toupper(root[0]) - 'A';
1029 path = &root[2];
1030 if (path[0] == '\0')
1031 path = DRIVE_GetDosCwd(drive);
1032 else
1033 if (path[0] == '\\')
1034 path++;
1035 if (strlen(path)) /* oops, we are in a subdir */
1036 return FALSE;
1038 else
1039 return FALSE;
1041 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1043 /* Cap the size and available at 2GB as per specs. */
1044 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1046 size.s.HighPart = 0;
1047 size.s.LowPart = 0x7fffffff;
1049 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1051 available.s.HighPart =0;
1052 available.s.LowPart = 0x7fffffff;
1054 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1055 size.s.LowPart /= sec_size;
1056 available.s.LowPart /= sec_size;
1057 /* fixme: probably have to adjust those variables too for CDFS */
1058 cluster_sec = 1;
1059 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1061 if (cluster_sectors)
1062 *cluster_sectors = cluster_sec;
1063 if (sector_bytes)
1064 *sector_bytes = sec_size;
1065 if (free_clusters)
1066 *free_clusters = available.s.LowPart / cluster_sec;
1067 if (total_clusters)
1068 *total_clusters = size.s.LowPart / cluster_sec;
1069 return TRUE;
1073 /***********************************************************************
1074 * GetDiskFreeSpaceW (KERNEL32.@)
1076 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1077 LPDWORD sector_bytes, LPDWORD free_clusters,
1078 LPDWORD total_clusters )
1080 LPSTR xroot;
1081 BOOL ret;
1083 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1084 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
1085 free_clusters, total_clusters );
1086 HeapFree( GetProcessHeap(), 0, xroot );
1087 return ret;
1091 /***********************************************************************
1092 * GetDiskFreeSpaceExA (KERNEL32.@)
1094 * This function is used to acquire the size of the available and
1095 * total space on a logical volume.
1097 * RETURNS
1099 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1100 * detailed error information.
1103 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1104 PULARGE_INTEGER avail,
1105 PULARGE_INTEGER total,
1106 PULARGE_INTEGER totalfree)
1108 int drive;
1109 ULARGE_INTEGER size,available;
1111 if (!root) drive = DRIVE_GetCurrentDrive();
1112 else
1114 if ((root[1]) && ((root[1] != ':') || (root[2] != '\\')))
1116 FIXME("there are valid root names which are not supported yet\n");
1117 /* ..like UNC names, for instance. */
1119 WARN("invalid root '%s'\n", root );
1120 return FALSE;
1122 drive = toupper(root[0]) - 'A';
1125 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1127 if (total)
1129 total->s.HighPart = size.s.HighPart;
1130 total->s.LowPart = size.s.LowPart;
1133 if (totalfree)
1135 totalfree->s.HighPart = available.s.HighPart;
1136 totalfree->s.LowPart = available.s.LowPart;
1139 if (avail)
1141 if (FIXME_ON(dosfs))
1143 /* On Windows2000, we need to check the disk quota
1144 allocated for the user owning the calling process. We
1145 don't want to be more obtrusive than necessary with the
1146 FIXME messages, so don't print the FIXME unless Wine is
1147 actually masquerading as Windows2000. */
1149 OSVERSIONINFOA ovi;
1150 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1151 if (GetVersionExA(&ovi))
1153 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1154 FIXME("no per-user quota support yet\n");
1158 /* Quick hack, should eventually be fixed to work 100% with
1159 Windows2000 (see comment above). */
1160 avail->s.HighPart = available.s.HighPart;
1161 avail->s.LowPart = available.s.LowPart;
1164 return TRUE;
1167 /***********************************************************************
1168 * GetDiskFreeSpaceExW (KERNEL32.@)
1170 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1171 PULARGE_INTEGER total,
1172 PULARGE_INTEGER totalfree)
1174 LPSTR xroot;
1175 BOOL ret;
1177 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1178 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1179 HeapFree( GetProcessHeap(), 0, xroot );
1180 return ret;
1183 /***********************************************************************
1184 * GetDriveType (KERNEL.136)
1185 * This function returns the type of a drive in Win16.
1186 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1187 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1188 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1189 * do any pseudo-clever changes.
1191 * RETURNS
1192 * drivetype DRIVE_xxx
1194 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1196 UINT type = DRIVE_GetType(drive);
1197 TRACE("(%c:)\n", 'A' + drive );
1198 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1199 return type;
1203 /***********************************************************************
1204 * GetDriveTypeA (KERNEL32.@)
1206 * Returns the type of the disk drive specified. If root is NULL the
1207 * root of the current directory is used.
1209 * RETURNS
1211 * Type of drive (from Win32 SDK):
1213 * DRIVE_UNKNOWN unable to find out anything about the drive
1214 * DRIVE_NO_ROOT_DIR nonexistent root dir
1215 * DRIVE_REMOVABLE the disk can be removed from the machine
1216 * DRIVE_FIXED the disk can not be removed from the machine
1217 * DRIVE_REMOTE network disk
1218 * DRIVE_CDROM CDROM drive
1219 * DRIVE_RAMDISK virtual disk in RAM
1221 UINT WINAPI GetDriveTypeA(LPCSTR root) /* [in] String describing drive */
1223 int drive;
1224 TRACE("(%s)\n", debugstr_a(root));
1226 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1227 else
1229 if ((root[1]) && (root[1] != ':'))
1231 WARN("invalid root %s\n", debugstr_a(root));
1232 return DRIVE_NO_ROOT_DIR;
1234 drive = toupper(root[0]) - 'A';
1236 return DRIVE_GetType(drive);
1240 /***********************************************************************
1241 * GetDriveTypeW (KERNEL32.@)
1243 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1245 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1246 UINT ret = GetDriveTypeA( xpath );
1247 HeapFree( GetProcessHeap(), 0, xpath );
1248 return ret;
1252 /***********************************************************************
1253 * GetCurrentDirectory (KERNEL.411)
1255 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1257 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1261 /***********************************************************************
1262 * GetCurrentDirectoryA (KERNEL32.@)
1264 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1266 UINT ret;
1267 char longname[MAX_PATHNAME_LEN];
1268 char shortname[MAX_PATHNAME_LEN];
1269 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1270 if ( ret > MAX_PATHNAME_LEN ) {
1271 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1272 return ret;
1274 GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1275 ret = strlen( longname ) + 1;
1276 if (ret > buflen) return ret;
1277 strcpy(buf, longname);
1278 return ret - 1;
1281 /***********************************************************************
1282 * GetCurrentDirectoryW (KERNEL32.@)
1284 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1286 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1287 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1288 if (ret < buflen) ret = MultiByteToWideChar( CP_ACP, 0, xpath, -1, buf, buflen ) - 1;
1289 HeapFree( GetProcessHeap(), 0, xpath );
1290 return ret;
1294 /***********************************************************************
1295 * SetCurrentDirectory (KERNEL.412)
1297 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1299 return SetCurrentDirectoryA( dir );
1303 /***********************************************************************
1304 * SetCurrentDirectoryA (KERNEL32.@)
1306 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1308 int drive, olddrive = DRIVE_GetCurrentDrive();
1310 if (!dir) {
1311 ERR_(file)("(NULL)!\n");
1312 return FALSE;
1314 if (dir[0] && (dir[1]==':'))
1316 drive = toupper( *dir ) - 'A';
1317 dir += 2;
1319 else
1320 drive = olddrive;
1322 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1323 sets pTask->curdir only if pTask->curdrive is drive */
1324 if (!(DRIVE_SetCurrentDrive( drive )))
1325 return FALSE;
1326 /* FIXME: what about empty strings? Add a \\ ? */
1327 if (!DRIVE_Chdir( drive, dir )) {
1328 DRIVE_SetCurrentDrive(olddrive);
1329 return FALSE;
1331 return TRUE;
1335 /***********************************************************************
1336 * SetCurrentDirectoryW (KERNEL32.@)
1338 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1340 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1341 BOOL res = SetCurrentDirectoryA( dir );
1342 HeapFree( GetProcessHeap(), 0, dir );
1343 return res;
1347 /***********************************************************************
1348 * GetLogicalDriveStringsA (KERNEL32.@)
1350 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1352 int drive, count;
1354 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1355 if (DRIVE_IsValid(drive)) count++;
1356 if ((count * 4) + 1 <= len)
1358 LPSTR p = buffer;
1359 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1360 if (DRIVE_IsValid(drive))
1362 *p++ = 'a' + drive;
1363 *p++ = ':';
1364 *p++ = '\\';
1365 *p++ = '\0';
1367 *p = '\0';
1368 return count * 4;
1370 else
1371 return (count * 4) + 1; /* account for terminating null */
1372 /* The API tells about these different return values */
1376 /***********************************************************************
1377 * GetLogicalDriveStringsW (KERNEL32.@)
1379 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1381 int drive, count;
1383 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1384 if (DRIVE_IsValid(drive)) count++;
1385 if (count * 4 * sizeof(WCHAR) <= len)
1387 LPWSTR p = buffer;
1388 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1389 if (DRIVE_IsValid(drive))
1391 *p++ = (WCHAR)('a' + drive);
1392 *p++ = (WCHAR)':';
1393 *p++ = (WCHAR)'\\';
1394 *p++ = (WCHAR)'\0';
1396 *p = (WCHAR)'\0';
1398 return count * 4 * sizeof(WCHAR);
1402 /***********************************************************************
1403 * GetLogicalDrives (KERNEL32.@)
1405 DWORD WINAPI GetLogicalDrives(void)
1407 DWORD ret = 0;
1408 int drive;
1410 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1412 if ( (DRIVE_IsValid(drive)) ||
1413 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1414 ret |= (1 << drive);
1416 return ret;
1420 /***********************************************************************
1421 * GetVolumeInformationA (KERNEL32.@)
1423 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1424 DWORD label_len, DWORD *serial,
1425 DWORD *filename_len, DWORD *flags,
1426 LPSTR fsname, DWORD fsname_len )
1428 int drive;
1429 char *cp;
1431 /* FIXME, SetLastError()s missing */
1433 if (!root) drive = DRIVE_GetCurrentDrive();
1434 else
1436 if ((root[1]) && (root[1] != ':'))
1438 WARN("invalid root '%s'\n",root);
1439 return FALSE;
1441 drive = toupper(root[0]) - 'A';
1443 if (!DRIVE_IsValid( drive )) return FALSE;
1444 if (label)
1446 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1447 cp = label + strlen(label);
1448 while (cp != label && *(cp-1) == ' ') cp--;
1449 *cp = '\0';
1451 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1453 /* Set the filesystem information */
1454 /* Note: we only emulate a FAT fs at present */
1456 if (filename_len) {
1457 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1458 *filename_len = 12;
1459 else
1460 *filename_len = 255;
1462 if (flags)
1464 *flags=0;
1465 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1466 *flags|=FS_CASE_SENSITIVE;
1467 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1468 *flags|=FS_CASE_IS_PRESERVED;
1470 if (fsname) {
1471 /* Diablo checks that return code ... */
1472 if (DOSDrives[drive].type == DRIVE_CDROM)
1473 lstrcpynA( fsname, "CDFS", fsname_len );
1474 else
1475 lstrcpynA( fsname, "FAT", fsname_len );
1477 return TRUE;
1481 /***********************************************************************
1482 * GetVolumeInformationW (KERNEL32.@)
1484 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1485 DWORD label_len, DWORD *serial,
1486 DWORD *filename_len, DWORD *flags,
1487 LPWSTR fsname, DWORD fsname_len )
1489 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1490 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1491 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1492 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1493 filename_len, flags, xfsname,
1494 fsname_len );
1495 if (ret)
1497 if (label) MultiByteToWideChar( CP_ACP, 0, xvolname, -1, label, label_len );
1498 if (fsname) MultiByteToWideChar( CP_ACP, 0, xfsname, -1, fsname, fsname_len );
1500 HeapFree( GetProcessHeap(), 0, xroot );
1501 HeapFree( GetProcessHeap(), 0, xvolname );
1502 HeapFree( GetProcessHeap(), 0, xfsname );
1503 return ret;
1506 /***********************************************************************
1507 * SetVolumeLabelA (KERNEL32.@)
1509 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1511 int drive;
1513 /* FIXME, SetLastErrors missing */
1515 if (!root) drive = DRIVE_GetCurrentDrive();
1516 else
1518 if ((root[1]) && (root[1] != ':'))
1520 WARN("invalid root '%s'\n",root);
1521 return FALSE;
1523 drive = toupper(root[0]) - 'A';
1525 if (!DRIVE_IsValid( drive )) return FALSE;
1527 /* some copy protection stuff check this */
1528 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
1530 FIXME("(%s,%s),stub!\n", root, volname);
1531 return TRUE;
1534 /***********************************************************************
1535 * SetVolumeLabelW (KERNEL32.@)
1537 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1539 LPSTR xroot, xvol;
1540 BOOL ret;
1542 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1543 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1544 ret = SetVolumeLabelA( xroot, xvol );
1545 HeapFree( GetProcessHeap(), 0, xroot );
1546 HeapFree( GetProcessHeap(), 0, xvol );
1547 return ret;