Create GDI stock objects as normal objects instead of using magic
[wine/testsucceed.git] / files / drive.c
blobb294389c514101fda8aca49ee4b608d8ffe3207b
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"
55 DEFAULT_DEBUG_CHANNEL(dosfs);
56 DECLARE_DEBUG_CHANNEL(file);
58 typedef struct
60 char *root; /* root dir in Unix format without trailing / */
61 char *dos_cwd; /* cwd in DOS format without leading or trailing \ */
62 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
63 char *device; /* raw device path */
64 char label_conf[12]; /* drive label as cfg'd in wine config */
65 char label_read[12]; /* drive label as read from device */
66 DWORD serial_conf; /* drive serial number as cfg'd in wine config */
67 UINT 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 "", /* DRIVE_UNKNOWN */
77 "", /* DRIVE_NO_ROOT_DIR */
78 "floppy", /* DRIVE_REMOVABLE */
79 "hd", /* DRIVE_FIXED */
80 "network", /* DRIVE_REMOTE */
81 "cdrom", /* DRIVE_CDROM */
82 "ramdisk" /* DRIVE_RAMDISK */
86 /* Known filesystem types */
88 typedef struct
90 const char *name;
91 UINT flags;
92 } FS_DESCR;
94 static const FS_DESCR DRIVE_Filesystems[] =
96 { "unix", DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
97 { "msdos", DRIVE_SHORT_NAMES },
98 { "dos", DRIVE_SHORT_NAMES },
99 { "fat", DRIVE_SHORT_NAMES },
100 { "vfat", DRIVE_CASE_PRESERVING },
101 { "win95", DRIVE_CASE_PRESERVING },
102 { NULL, 0 }
106 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
107 static int DRIVE_CurDrive = -1;
109 static HTASK16 DRIVE_LastTask = 0;
111 /* strdup on the process heap */
112 inline static char *heap_strdup( const char *str )
114 INT len = strlen(str) + 1;
115 LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
116 if (p) memcpy( p, str, len );
117 return p;
120 /***********************************************************************
121 * DRIVE_GetDriveType
123 static UINT DRIVE_GetDriveType( const char *name )
125 char buffer[20];
126 int i;
128 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
129 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
131 if (!strcasecmp( buffer, DRIVE_Types[i] )) return i;
133 MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
134 name, buffer );
135 return DRIVE_FIXED;
139 /***********************************************************************
140 * DRIVE_GetFSFlags
142 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
144 const FS_DESCR *descr;
146 for (descr = DRIVE_Filesystems; descr->name; descr++)
147 if (!strcasecmp( value, descr->name )) return descr->flags;
148 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
149 name, value );
150 return DRIVE_CASE_PRESERVING;
154 /***********************************************************************
155 * DRIVE_Init
157 int DRIVE_Init(void)
159 int i, len, count = 0;
160 char name[] = "Drive A";
161 char drive_env[] = "=A:";
162 char path[MAX_PATHNAME_LEN];
163 char buffer[80];
164 struct stat drive_stat_buffer;
165 char *p;
166 DOSDRIVE *drive;
168 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
170 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
171 if (path[0])
173 p = path + strlen(path) - 1;
174 while ((p > path) && ((*p == '/') || (*p == '\\'))) *p-- = '\0';
175 if (!path[0]) strcpy( path, "/" );
177 if (stat( path, &drive_stat_buffer ))
179 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
180 path, strerror(errno), 'A' + i);
181 continue;
183 if (!S_ISDIR(drive_stat_buffer.st_mode))
185 MESSAGE("%s is not a directory, ignoring drive %c:\n",
186 path, 'A' + i );
187 continue;
190 drive->root = heap_strdup( path );
191 drive->dos_cwd = heap_strdup( "" );
192 drive->unix_cwd = heap_strdup( "" );
193 drive->type = DRIVE_GetDriveType( name );
194 drive->device = NULL;
195 drive->flags = 0;
196 drive->dev = drive_stat_buffer.st_dev;
197 drive->ino = drive_stat_buffer.st_ino;
199 /* Get the drive label */
200 PROFILE_GetWineIniString( name, "Label", "", drive->label_conf, 12 );
201 if ((len = strlen(drive->label_conf)) < 11)
203 /* Pad label with spaces */
204 memset( drive->label_conf + len, ' ', 11 - len );
205 drive->label_conf[11] = '\0';
208 /* Get the serial number */
209 PROFILE_GetWineIniString( name, "Serial", "12345678",
210 buffer, sizeof(buffer) );
211 drive->serial_conf = strtoul( buffer, NULL, 16 );
213 /* Get the filesystem type */
214 PROFILE_GetWineIniString( name, "Filesystem", "win95",
215 buffer, sizeof(buffer) );
216 drive->flags = DRIVE_GetFSFlags( name, buffer );
218 /* Get the device */
219 PROFILE_GetWineIniString( name, "Device", "",
220 buffer, sizeof(buffer) );
221 if (buffer[0])
223 drive->device = heap_strdup( buffer );
224 if (PROFILE_GetWineIniBool( name, "ReadVolInfo", 1))
225 drive->flags |= DRIVE_READ_VOL_INFO;
228 /* Get the FailReadOnly flag */
229 if (PROFILE_GetWineIniBool( name, "FailReadOnly", 0 ))
230 drive->flags |= DRIVE_FAIL_READ_ONLY;
232 /* Make the first hard disk the current drive */
233 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
234 DRIVE_CurDrive = i;
236 count++;
237 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
238 "flags=%08x dev=%x ino=%x\n",
239 name, path, DRIVE_Types[drive->type],
240 drive->label_conf, drive->serial_conf, drive->flags,
241 (int)drive->dev, (int)drive->ino );
243 else WARN("%s: not defined\n", name );
246 if (!count)
248 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
249 /* Create a C drive pointing to Unix root dir */
250 DOSDrives[2].root = heap_strdup( "/" );
251 DOSDrives[2].dos_cwd = heap_strdup( "" );
252 DOSDrives[2].unix_cwd = heap_strdup( "" );
253 strcpy( DOSDrives[2].label_conf, "Drive C " );
254 DOSDrives[2].serial_conf = 12345678;
255 DOSDrives[2].type = DRIVE_FIXED;
256 DOSDrives[2].device = NULL;
257 DOSDrives[2].flags = 0;
258 DRIVE_CurDrive = 2;
261 /* Make sure the current drive is valid */
262 if (DRIVE_CurDrive == -1)
264 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
266 if (drive->root && !(drive->flags & DRIVE_DISABLED))
268 DRIVE_CurDrive = i;
269 break;
274 /* get current working directory info for all drives */
275 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
277 if (!GetEnvironmentVariableA(drive_env, path, sizeof(path))) continue;
278 /* sanity check */
279 if (toupper(path[0]) != drive_env[1] || path[1] != ':') continue;
280 DRIVE_Chdir( i, path + 2 );
282 return 1;
286 /***********************************************************************
287 * DRIVE_IsValid
289 int DRIVE_IsValid( int drive )
291 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
292 return (DOSDrives[drive].root &&
293 !(DOSDrives[drive].flags & DRIVE_DISABLED));
297 /***********************************************************************
298 * DRIVE_GetCurrentDrive
300 int DRIVE_GetCurrentDrive(void)
302 TDB *pTask = TASK_GetCurrent();
303 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
304 return DRIVE_CurDrive;
308 /***********************************************************************
309 * DRIVE_SetCurrentDrive
311 int DRIVE_SetCurrentDrive( int drive )
313 TDB *pTask = TASK_GetCurrent();
314 if (!DRIVE_IsValid( drive ))
316 SetLastError( ERROR_INVALID_DRIVE );
317 return 0;
319 TRACE("%c:\n", 'A' + drive );
320 DRIVE_CurDrive = drive;
321 if (pTask) pTask->curdrive = drive | 0x80;
322 chdir(DRIVE_GetUnixCwd(drive));
323 return 1;
327 /***********************************************************************
328 * DRIVE_FindDriveRoot
330 * Find a drive for which the root matches the beginning of the given path.
331 * This can be used to translate a Unix path into a drive + DOS path.
332 * Return value is the drive, or -1 on error. On success, path is modified
333 * to point to the beginning of the DOS path.
335 int DRIVE_FindDriveRoot( const char **path )
337 /* idea: check at all '/' positions.
338 * If the device and inode of that path is identical with the
339 * device and inode of the current drive then we found a solution.
340 * If there is another drive pointing to a deeper position in
341 * the file tree, we want to find that one, not the earlier solution.
343 int drive, rootdrive = -1;
344 char buffer[MAX_PATHNAME_LEN];
345 char *next = buffer;
346 const char *p = *path;
347 struct stat st;
349 strcpy( buffer, "/" );
350 for (;;)
352 if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
354 /* Find the drive */
356 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
358 if (!DOSDrives[drive].root ||
359 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
361 if ((DOSDrives[drive].dev == st.st_dev) &&
362 (DOSDrives[drive].ino == st.st_ino))
364 rootdrive = drive;
365 *path = p;
366 break;
370 /* Get the next path component */
372 *next++ = '/';
373 while ((*p == '/') || (*p == '\\')) p++;
374 if (!*p) break;
375 while (!IS_END_OF_NAME(*p)) *next++ = *p++;
376 *next = 0;
378 *next = 0;
380 if (rootdrive != -1)
381 TRACE("%s -> drive %c:, root='%s', name='%s'\n",
382 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, *path );
383 return rootdrive;
387 /***********************************************************************
388 * DRIVE_GetRoot
390 const char * DRIVE_GetRoot( int drive )
392 if (!DRIVE_IsValid( drive )) return NULL;
393 return DOSDrives[drive].root;
397 /***********************************************************************
398 * DRIVE_GetDosCwd
400 const char * DRIVE_GetDosCwd( int drive )
402 TDB *pTask = TASK_GetCurrent();
403 if (!DRIVE_IsValid( drive )) return NULL;
405 /* Check if we need to change the directory to the new task. */
406 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
407 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
408 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
410 /* Perform the task-switch */
411 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
412 DRIVE_LastTask = GetCurrentTask();
414 return DOSDrives[drive].dos_cwd;
418 /***********************************************************************
419 * DRIVE_GetUnixCwd
421 const char * DRIVE_GetUnixCwd( int drive )
423 TDB *pTask = TASK_GetCurrent();
424 if (!DRIVE_IsValid( drive )) return NULL;
426 /* Check if we need to change the directory to the new task. */
427 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
428 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
429 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
431 /* Perform the task-switch */
432 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
433 DRIVE_LastTask = GetCurrentTask();
435 return DOSDrives[drive].unix_cwd;
439 /***********************************************************************
440 * DRIVE_GetDevice
442 const char * DRIVE_GetDevice( int drive )
444 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
448 /***********************************************************************
449 * DRIVE_ReadSuperblock
451 * NOTE
452 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
453 * to check, that they are writing on a FAT filesystem !
455 int DRIVE_ReadSuperblock (int drive, char * buff)
457 #define DRIVE_SUPER 96
458 int fd;
459 off_t offs;
461 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
462 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
464 struct stat st;
465 if (!DOSDrives[drive].device)
466 ERR("No device configured for drive %c: !\n", 'A'+drive);
467 else
468 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
469 (stat(DOSDrives[drive].device, &st)) ?
470 "not available or symlink not valid ?" : "no permission");
471 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
472 PROFILE_UsageWineIni();
473 return -1;
476 switch(DOSDrives[drive].type)
478 case DRIVE_REMOVABLE:
479 case DRIVE_FIXED:
480 offs = 0;
481 break;
482 case DRIVE_CDROM:
483 offs = CDROM_Data_FindBestVoldesc(fd);
484 break;
485 default:
486 offs = 0;
487 break;
490 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs)) return -4;
491 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER) return -2;
493 switch(DOSDrives[drive].type)
495 case DRIVE_REMOVABLE:
496 case DRIVE_FIXED:
497 if ((buff[0x26]!=0x29) || /* Check for FAT present */
498 /* FIXME: do really all FAT have their name beginning with
499 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
500 memcmp( buff+0x36,"FAT",3))
502 ERR("The filesystem is not FAT !! (device=%s)\n",
503 DOSDrives[drive].device);
504 return -3;
506 break;
507 case DRIVE_CDROM:
508 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
509 return -3;
510 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
511 break;
512 default:
513 return -3;
514 break;
517 return close(fd);
521 /***********************************************************************
522 * DRIVE_WriteSuperblockEntry
524 * NOTE
525 * We are writing as little as possible (ie. not the whole SuperBlock)
526 * not to interfere with kernel. The drive can be mounted !
528 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
530 int fd;
532 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
534 ERR("Cannot open the device %s (for writing)\n",
535 DOSDrives[drive].device);
536 return -1;
538 if (lseek(fd,ofs,SEEK_SET)!=ofs)
540 ERR("lseek failed on device %s !\n",
541 DOSDrives[drive].device);
542 close(fd);
543 return -2;
545 if (write(fd,buff,len)!=len)
547 close(fd);
548 ERR("Cannot write on %s !\n",
549 DOSDrives[drive].device);
550 return -3;
552 return close (fd);
557 /***********************************************************************
558 * DRIVE_GetLabel
560 const char * DRIVE_GetLabel( int drive )
562 int read = 0;
563 char buff[DRIVE_SUPER];
564 int offs = -1;
566 if (!DRIVE_IsValid( drive )) return NULL;
567 if (DOSDrives[drive].type == DRIVE_CDROM)
569 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
571 else
572 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
574 if (DRIVE_ReadSuperblock(drive,(char *) buff))
575 ERR("Invalid or unreadable superblock on %s (%c:).\n",
576 DOSDrives[drive].device, (char)(drive+'A'));
577 else {
578 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
579 DOSDrives[drive].type == DRIVE_FIXED)
580 offs = 0x2b;
582 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
583 if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
584 DOSDrives[drive].label_read[11]='\0';
585 read = 1;
589 return (read) ?
590 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
594 /***********************************************************************
595 * DRIVE_GetSerialNumber
597 DWORD DRIVE_GetSerialNumber( int drive )
599 DWORD serial = 0;
600 char buff[DRIVE_SUPER];
602 if (!DRIVE_IsValid( drive )) return 0;
604 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
606 switch(DOSDrives[drive].type)
608 case DRIVE_REMOVABLE:
609 case DRIVE_FIXED:
610 if (DRIVE_ReadSuperblock(drive,(char *) buff))
611 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
612 " Maybe not FAT?\n" ,
613 DOSDrives[drive].device, 'A'+drive);
614 else
615 serial = *((DWORD*)(buff+0x27));
616 break;
617 case DRIVE_CDROM:
618 serial = CDROM_GetSerial(drive);
619 break;
620 default:
621 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
625 return (serial) ? serial : DOSDrives[drive].serial_conf;
629 /***********************************************************************
630 * DRIVE_SetSerialNumber
632 int DRIVE_SetSerialNumber( int drive, DWORD serial )
634 char buff[DRIVE_SUPER];
636 if (!DRIVE_IsValid( drive )) return 0;
638 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
640 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
641 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
642 /* check, if the drive has a FAT filesystem */
643 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
644 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
645 return 1;
648 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
649 DOSDrives[drive].serial_conf = serial;
650 return 1;
654 /***********************************************************************
655 * DRIVE_GetType
657 static UINT DRIVE_GetType( int drive )
659 if (!DRIVE_IsValid( drive )) return DRIVE_UNKNOWN;
660 return DOSDrives[drive].type;
664 /***********************************************************************
665 * DRIVE_GetFlags
667 UINT DRIVE_GetFlags( int drive )
669 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
670 return DOSDrives[drive].flags;
674 /***********************************************************************
675 * DRIVE_Chdir
677 int DRIVE_Chdir( int drive, const char *path )
679 DOS_FULL_NAME full_name;
680 char buffer[MAX_PATHNAME_LEN];
681 LPSTR unix_cwd;
682 BY_HANDLE_FILE_INFORMATION info;
683 TDB *pTask = TASK_GetCurrent();
685 strcpy( buffer, "A:" );
686 buffer[0] += drive;
687 TRACE("(%s,%s)\n", buffer, path );
688 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
690 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
691 if (!FILE_Stat( full_name.long_name, &info )) return 0;
692 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
694 SetLastError( ERROR_FILE_NOT_FOUND );
695 return 0;
697 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
698 while (*unix_cwd == '/') unix_cwd++;
700 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
701 'A' + drive, unix_cwd, full_name.short_name + 3 );
703 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
704 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
705 DOSDrives[drive].dos_cwd = heap_strdup( full_name.short_name + 3 );
706 DOSDrives[drive].unix_cwd = heap_strdup( 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();
714 chdir(unix_cwd); /* Only change if on current drive */
716 return 1;
720 /***********************************************************************
721 * DRIVE_Disable
723 int DRIVE_Disable( int drive )
725 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
727 SetLastError( ERROR_INVALID_DRIVE );
728 return 0;
730 DOSDrives[drive].flags |= DRIVE_DISABLED;
731 return 1;
735 /***********************************************************************
736 * DRIVE_Enable
738 int DRIVE_Enable( 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_SetLogicalMapping
753 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
755 /* If new_drive is already valid, do nothing and return 0
756 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
758 DOSDRIVE *old, *new;
760 old = DOSDrives + existing_drive;
761 new = DOSDrives + new_drive;
763 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
764 !old->root ||
765 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
767 SetLastError( ERROR_INVALID_DRIVE );
768 return 0;
771 if ( new->root )
773 TRACE("Can't map drive %c: to already existing drive %c:\n",
774 'A' + existing_drive, 'A' + new_drive );
775 /* it is already mapped there, so return success */
776 if (!strcmp(old->root,new->root))
777 return 1;
778 return 0;
781 new->root = heap_strdup( old->root );
782 new->dos_cwd = heap_strdup( old->dos_cwd );
783 new->unix_cwd = heap_strdup( old->unix_cwd );
784 new->device = heap_strdup( old->device );
785 memcpy ( new->label_conf, old->label_conf, 12 );
786 memcpy ( new->label_read, old->label_read, 12 );
787 new->serial_conf = old->serial_conf;
788 new->type = old->type;
789 new->flags = old->flags;
790 new->dev = old->dev;
791 new->ino = old->ino;
793 TRACE("Drive %c: is now equal to drive %c:\n",
794 'A' + new_drive, 'A' + existing_drive );
796 return 1;
800 /***********************************************************************
801 * DRIVE_OpenDevice
803 * Open the drive raw device and return a Unix fd (or -1 on error).
805 int DRIVE_OpenDevice( int drive, int flags )
807 if (!DRIVE_IsValid( drive )) return -1;
808 return open( DOSDrives[drive].device, flags );
812 /***********************************************************************
813 * DRIVE_RawRead
815 * Read raw sectors from a device
817 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
819 int fd;
821 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
823 lseek( fd, begin * 512, SEEK_SET );
824 /* FIXME: check errors */
825 read( fd, dataptr, nr_sect * 512 );
826 close( fd );
828 else
830 memset(dataptr, 0, nr_sect * 512);
831 if (fake_success)
833 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
834 if (begin == 1) *dataptr = 0xf8;
836 else
837 return 0;
839 return 1;
843 /***********************************************************************
844 * DRIVE_RawWrite
846 * Write raw sectors to a device
848 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
850 int fd;
852 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
854 lseek( fd, begin * 512, SEEK_SET );
855 /* FIXME: check errors */
856 write( fd, dataptr, nr_sect * 512 );
857 close( fd );
859 else
860 if (!(fake_success))
861 return 0;
863 return 1;
867 /***********************************************************************
868 * DRIVE_GetFreeSpace
870 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
871 PULARGE_INTEGER available )
873 struct statfs info;
875 if (!DRIVE_IsValid(drive))
877 SetLastError( ERROR_INVALID_DRIVE );
878 return 0;
881 /* FIXME: add autoconf check for this */
882 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
883 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
884 #else
885 if (statfs( DOSDrives[drive].root, &info) < 0)
886 #endif
888 FILE_SetDosError();
889 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
890 return 0;
893 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
894 #ifdef STATFS_HAS_BAVAIL
895 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
896 #else
897 # ifdef STATFS_HAS_BFREE
898 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
899 # else
900 # error "statfs has no bfree/bavail member!"
901 # endif
902 #endif
903 if (DOSDrives[drive].type == DRIVE_CDROM)
904 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
905 available->QuadPart = 0;
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;
933 /***********************************************************************
934 * DRIVE_BuildEnv
936 * Build the environment array containing the drives current directories.
937 * Resulting pointer must be freed with HeapFree.
939 char *DRIVE_BuildEnv(void)
941 int i, length = 0;
942 const char *cwd[MAX_DOS_DRIVES];
943 char *env, *p;
945 for (i = 0; i < MAX_DOS_DRIVES; i++)
947 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0]) length += strlen(cwd[i]) + 8;
949 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
950 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
952 if (cwd[i] && cwd[i][0])
953 p += sprintf( p, "=%c:=%c:\\%s", 'A'+i, 'A'+i, cwd[i] ) + 1;
955 *p = 0;
956 return env;
960 /***********************************************************************
961 * GetDiskFreeSpace (KERNEL.422)
963 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
964 LPDWORD sector_bytes, LPDWORD free_clusters,
965 LPDWORD total_clusters )
967 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
968 free_clusters, total_clusters );
972 /***********************************************************************
973 * GetDiskFreeSpaceA (KERNEL32.@)
975 * Fails if expression resulting from current drive's dir and "root"
976 * is not a root dir of the target drive.
978 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
979 * if the corresponding info is unneeded.
981 * FIXME: needs to support UNC names from Win95 OSR2 on.
983 * Behaviour under Win95a:
984 * CurrDir root result
985 * "E:\\TEST" "E:" FALSE
986 * "E:\\" "E:" TRUE
987 * "E:\\" "E" FALSE
988 * "E:\\" "\\" TRUE
989 * "E:\\TEST" "\\" TRUE
990 * "E:\\TEST" ":\\" FALSE
991 * "E:\\TEST" "E:\\" TRUE
992 * "E:\\TEST" "" FALSE
993 * "E:\\" "" FALSE (!)
994 * "E:\\" 0x0 TRUE
995 * "E:\\TEST" 0x0 TRUE (!)
996 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
997 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
999 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1000 LPDWORD sector_bytes, LPDWORD free_clusters,
1001 LPDWORD total_clusters )
1003 int drive, sec_size;
1004 ULARGE_INTEGER size,available;
1005 LPCSTR path;
1006 DWORD cluster_sec;
1008 if ((!root) || (strcmp(root,"\\") == 0))
1009 drive = DRIVE_GetCurrentDrive();
1010 else
1011 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
1013 drive = toupper(root[0]) - 'A';
1014 path = &root[2];
1015 if (path[0] == '\0')
1016 path = DRIVE_GetDosCwd(drive);
1017 else
1018 if (path[0] == '\\')
1019 path++;
1020 if (strlen(path)) /* oops, we are in a subdir */
1021 return FALSE;
1023 else
1024 return FALSE;
1026 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1028 /* Cap the size and available at 2GB as per specs. */
1029 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1031 size.s.HighPart = 0;
1032 size.s.LowPart = 0x7fffffff;
1034 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1036 available.s.HighPart =0;
1037 available.s.LowPart = 0x7fffffff;
1039 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1040 size.s.LowPart /= sec_size;
1041 available.s.LowPart /= sec_size;
1042 /* fixme: probably have to adjust those variables too for CDFS */
1043 cluster_sec = 1;
1044 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1046 if (cluster_sectors)
1047 *cluster_sectors = cluster_sec;
1048 if (sector_bytes)
1049 *sector_bytes = sec_size;
1050 if (free_clusters)
1051 *free_clusters = available.s.LowPart / cluster_sec;
1052 if (total_clusters)
1053 *total_clusters = size.s.LowPart / cluster_sec;
1054 return TRUE;
1058 /***********************************************************************
1059 * GetDiskFreeSpaceW (KERNEL32.@)
1061 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1062 LPDWORD sector_bytes, LPDWORD free_clusters,
1063 LPDWORD total_clusters )
1065 LPSTR xroot;
1066 BOOL ret;
1068 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1069 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
1070 free_clusters, total_clusters );
1071 HeapFree( GetProcessHeap(), 0, xroot );
1072 return ret;
1076 /***********************************************************************
1077 * GetDiskFreeSpaceExA (KERNEL32.@)
1079 * This function is used to acquire the size of the available and
1080 * total space on a logical volume.
1082 * RETURNS
1084 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1085 * detailed error information.
1088 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1089 PULARGE_INTEGER avail,
1090 PULARGE_INTEGER total,
1091 PULARGE_INTEGER totalfree)
1093 int drive;
1094 ULARGE_INTEGER size,available;
1096 if (!root) drive = DRIVE_GetCurrentDrive();
1097 else
1099 if ((root[1]) && ((root[1] != ':') || (root[2] != '\\')))
1101 FIXME("there are valid root names which are not supported yet\n");
1102 /* ..like UNC names, for instance. */
1104 WARN("invalid root '%s'\n", root );
1105 return FALSE;
1107 drive = toupper(root[0]) - 'A';
1110 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1112 if (total)
1114 total->s.HighPart = size.s.HighPart;
1115 total->s.LowPart = size.s.LowPart;
1118 if (totalfree)
1120 totalfree->s.HighPart = available.s.HighPart;
1121 totalfree->s.LowPart = available.s.LowPart;
1124 if (avail)
1126 if (FIXME_ON(dosfs))
1128 /* On Windows2000, we need to check the disk quota
1129 allocated for the user owning the calling process. We
1130 don't want to be more obtrusive than necessary with the
1131 FIXME messages, so don't print the FIXME unless Wine is
1132 actually masquerading as Windows2000. */
1134 OSVERSIONINFOA ovi;
1135 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1136 if (GetVersionExA(&ovi))
1138 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1139 FIXME("no per-user quota support yet\n");
1143 /* Quick hack, should eventually be fixed to work 100% with
1144 Windows2000 (see comment above). */
1145 avail->s.HighPart = available.s.HighPart;
1146 avail->s.LowPart = available.s.LowPart;
1149 return TRUE;
1152 /***********************************************************************
1153 * GetDiskFreeSpaceExW (KERNEL32.@)
1155 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1156 PULARGE_INTEGER total,
1157 PULARGE_INTEGER totalfree)
1159 LPSTR xroot;
1160 BOOL ret;
1162 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1163 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1164 HeapFree( GetProcessHeap(), 0, xroot );
1165 return ret;
1168 /***********************************************************************
1169 * GetDriveType (KERNEL.136)
1170 * This function returns the type of a drive in Win16.
1171 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1172 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1173 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1174 * do any pseudo-clever changes.
1176 * RETURNS
1177 * drivetype DRIVE_xxx
1179 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1181 UINT type = DRIVE_GetType(drive);
1182 TRACE("(%c:)\n", 'A' + drive );
1183 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1184 return type;
1188 /***********************************************************************
1189 * GetDriveTypeA (KERNEL32.@)
1191 * Returns the type of the disk drive specified. If root is NULL the
1192 * root of the current directory is used.
1194 * RETURNS
1196 * Type of drive (from Win32 SDK):
1198 * DRIVE_UNKNOWN unable to find out anything about the drive
1199 * DRIVE_NO_ROOT_DIR nonexistent root dir
1200 * DRIVE_REMOVABLE the disk can be removed from the machine
1201 * DRIVE_FIXED the disk can not be removed from the machine
1202 * DRIVE_REMOTE network disk
1203 * DRIVE_CDROM CDROM drive
1204 * DRIVE_RAMDISK virtual disk in RAM
1206 UINT WINAPI GetDriveTypeA(LPCSTR root) /* [in] String describing drive */
1208 int drive;
1209 TRACE("(%s)\n", debugstr_a(root));
1211 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1212 else
1214 if ((root[1]) && (root[1] != ':'))
1216 WARN("invalid root %s\n", debugstr_a(root));
1217 return DRIVE_NO_ROOT_DIR;
1219 drive = toupper(root[0]) - 'A';
1221 return DRIVE_GetType(drive);
1225 /***********************************************************************
1226 * GetDriveTypeW (KERNEL32.@)
1228 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1230 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1231 UINT ret = GetDriveTypeA( xpath );
1232 HeapFree( GetProcessHeap(), 0, xpath );
1233 return ret;
1237 /***********************************************************************
1238 * GetCurrentDirectory (KERNEL.411)
1240 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1242 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1246 /***********************************************************************
1247 * GetCurrentDirectoryA (KERNEL32.@)
1249 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1251 UINT ret;
1252 char longname[MAX_PATHNAME_LEN];
1253 char shortname[MAX_PATHNAME_LEN];
1254 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1255 if ( ret > MAX_PATHNAME_LEN ) {
1256 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1257 return ret;
1259 GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1260 ret = strlen( longname ) + 1;
1261 if (ret > buflen) return ret;
1262 strcpy(buf, longname);
1263 return ret - 1;
1266 /***********************************************************************
1267 * GetCurrentDirectoryW (KERNEL32.@)
1269 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1271 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1272 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1273 if (ret < buflen) ret = MultiByteToWideChar( CP_ACP, 0, xpath, -1, buf, buflen ) - 1;
1274 HeapFree( GetProcessHeap(), 0, xpath );
1275 return ret;
1279 /***********************************************************************
1280 * SetCurrentDirectory (KERNEL.412)
1282 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1284 return SetCurrentDirectoryA( dir );
1288 /***********************************************************************
1289 * SetCurrentDirectoryA (KERNEL32.@)
1291 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1293 int drive, olddrive = DRIVE_GetCurrentDrive();
1295 if (!dir) {
1296 ERR_(file)("(NULL)!\n");
1297 return FALSE;
1299 if (dir[0] && (dir[1]==':'))
1301 drive = toupper( *dir ) - 'A';
1302 dir += 2;
1304 else
1305 drive = olddrive;
1307 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1308 sets pTask->curdir only if pTask->curdrive is drive */
1309 if (!(DRIVE_SetCurrentDrive( drive )))
1310 return FALSE;
1311 /* FIXME: what about empty strings? Add a \\ ? */
1312 if (!DRIVE_Chdir( drive, dir )) {
1313 DRIVE_SetCurrentDrive(olddrive);
1314 return FALSE;
1316 return TRUE;
1320 /***********************************************************************
1321 * SetCurrentDirectoryW (KERNEL32.@)
1323 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1325 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1326 BOOL res = SetCurrentDirectoryA( dir );
1327 HeapFree( GetProcessHeap(), 0, dir );
1328 return res;
1332 /***********************************************************************
1333 * GetLogicalDriveStringsA (KERNEL32.@)
1335 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1337 int drive, count;
1339 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1340 if (DRIVE_IsValid(drive)) count++;
1341 if ((count * 4) + 1 <= len)
1343 LPSTR p = buffer;
1344 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1345 if (DRIVE_IsValid(drive))
1347 *p++ = 'a' + drive;
1348 *p++ = ':';
1349 *p++ = '\\';
1350 *p++ = '\0';
1352 *p = '\0';
1353 return count * 4;
1355 else
1356 return (count * 4) + 1; /* account for terminating null */
1357 /* The API tells about these different return values */
1361 /***********************************************************************
1362 * GetLogicalDriveStringsW (KERNEL32.@)
1364 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1366 int drive, count;
1368 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1369 if (DRIVE_IsValid(drive)) count++;
1370 if (count * 4 * sizeof(WCHAR) <= len)
1372 LPWSTR p = buffer;
1373 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1374 if (DRIVE_IsValid(drive))
1376 *p++ = (WCHAR)('a' + drive);
1377 *p++ = (WCHAR)':';
1378 *p++ = (WCHAR)'\\';
1379 *p++ = (WCHAR)'\0';
1381 *p = (WCHAR)'\0';
1383 return count * 4 * sizeof(WCHAR);
1387 /***********************************************************************
1388 * GetLogicalDrives (KERNEL32.@)
1390 DWORD WINAPI GetLogicalDrives(void)
1392 DWORD ret = 0;
1393 int drive;
1395 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1397 if ( (DRIVE_IsValid(drive)) ||
1398 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1399 ret |= (1 << drive);
1401 return ret;
1405 /***********************************************************************
1406 * GetVolumeInformationA (KERNEL32.@)
1408 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1409 DWORD label_len, DWORD *serial,
1410 DWORD *filename_len, DWORD *flags,
1411 LPSTR fsname, DWORD fsname_len )
1413 int drive;
1414 char *cp;
1416 /* FIXME, SetLastError()s missing */
1418 if (!root) drive = DRIVE_GetCurrentDrive();
1419 else
1421 if ((root[1]) && (root[1] != ':'))
1423 WARN("invalid root '%s'\n",root);
1424 return FALSE;
1426 drive = toupper(root[0]) - 'A';
1428 if (!DRIVE_IsValid( drive )) return FALSE;
1429 if (label)
1431 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1432 cp = label + strlen(label);
1433 while (cp != label && *(cp-1) == ' ') cp--;
1434 *cp = '\0';
1436 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1438 /* Set the filesystem information */
1439 /* Note: we only emulate a FAT fs at present */
1441 if (filename_len) {
1442 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1443 *filename_len = 12;
1444 else
1445 *filename_len = 255;
1447 if (flags)
1449 *flags=0;
1450 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1451 *flags|=FS_CASE_SENSITIVE;
1452 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1453 *flags|=FS_CASE_IS_PRESERVED;
1455 if (fsname) {
1456 /* Diablo checks that return code ... */
1457 if (DOSDrives[drive].type == DRIVE_CDROM)
1458 lstrcpynA( fsname, "CDFS", fsname_len );
1459 else
1460 lstrcpynA( fsname, "FAT", fsname_len );
1462 return TRUE;
1466 /***********************************************************************
1467 * GetVolumeInformationW (KERNEL32.@)
1469 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1470 DWORD label_len, DWORD *serial,
1471 DWORD *filename_len, DWORD *flags,
1472 LPWSTR fsname, DWORD fsname_len )
1474 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1475 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1476 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1477 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1478 filename_len, flags, xfsname,
1479 fsname_len );
1480 if (ret)
1482 if (label) MultiByteToWideChar( CP_ACP, 0, xvolname, -1, label, label_len );
1483 if (fsname) MultiByteToWideChar( CP_ACP, 0, xfsname, -1, fsname, fsname_len );
1485 HeapFree( GetProcessHeap(), 0, xroot );
1486 HeapFree( GetProcessHeap(), 0, xvolname );
1487 HeapFree( GetProcessHeap(), 0, xfsname );
1488 return ret;
1491 /***********************************************************************
1492 * SetVolumeLabelA (KERNEL32.@)
1494 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1496 int drive;
1498 /* FIXME, SetLastErrors missing */
1500 if (!root) drive = DRIVE_GetCurrentDrive();
1501 else
1503 if ((root[1]) && (root[1] != ':'))
1505 WARN("invalid root '%s'\n",root);
1506 return FALSE;
1508 drive = toupper(root[0]) - 'A';
1510 if (!DRIVE_IsValid( drive )) return FALSE;
1512 /* some copy protection stuff check this */
1513 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
1515 FIXME("(%s,%s),stub!\n", root, volname);
1516 return TRUE;
1519 /***********************************************************************
1520 * SetVolumeLabelW (KERNEL32.@)
1522 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1524 LPSTR xroot, xvol;
1525 BOOL ret;
1527 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1528 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1529 ret = SetVolumeLabelA( xroot, xvol );
1530 HeapFree( GetProcessHeap(), 0, xroot );
1531 HeapFree( GetProcessHeap(), 0, xvol );
1532 return ret;