Implement EnumPrinterDataEx{A|W}.
[wine.git] / files / drive.c
blob6dc727afb866c23ff3b7f123d4b10be6ee6b5c1e
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;
112 /***********************************************************************
113 * DRIVE_GetDriveType
115 static UINT DRIVE_GetDriveType( const char *name )
117 char buffer[20];
118 int i;
120 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
121 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
123 if (!strcasecmp( buffer, DRIVE_Types[i] )) return i;
125 MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
126 name, buffer );
127 return DRIVE_FIXED;
131 /***********************************************************************
132 * DRIVE_GetFSFlags
134 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
136 const FS_DESCR *descr;
138 for (descr = DRIVE_Filesystems; descr->name; descr++)
139 if (!strcasecmp( value, descr->name )) return descr->flags;
140 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
141 name, value );
142 return DRIVE_CASE_PRESERVING;
146 /***********************************************************************
147 * DRIVE_Init
149 int DRIVE_Init(void)
151 int i, len, count = 0;
152 char name[] = "Drive A";
153 char drive_env[] = "=A:";
154 char path[MAX_PATHNAME_LEN];
155 char buffer[80];
156 struct stat drive_stat_buffer;
157 char *p;
158 DOSDRIVE *drive;
160 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
162 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
163 if (path[0])
165 p = path + strlen(path) - 1;
166 while ((p > path) && ((*p == '/') || (*p == '\\'))) *p-- = '\0';
167 if (!path[0]) strcpy( path, "/" );
169 if (stat( path, &drive_stat_buffer ))
171 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
172 path, strerror(errno), 'A' + i);
173 continue;
175 if (!S_ISDIR(drive_stat_buffer.st_mode))
177 MESSAGE("%s is not a directory, ignoring drive %c:\n",
178 path, 'A' + i );
179 continue;
182 drive->root = HEAP_strdupA( GetProcessHeap(), 0, path );
183 drive->dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
184 drive->unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
185 drive->type = DRIVE_GetDriveType( name );
186 drive->device = NULL;
187 drive->flags = 0;
188 drive->dev = drive_stat_buffer.st_dev;
189 drive->ino = drive_stat_buffer.st_ino;
191 /* Get the drive label */
192 PROFILE_GetWineIniString( name, "Label", "", drive->label_conf, 12 );
193 if ((len = strlen(drive->label_conf)) < 11)
195 /* Pad label with spaces */
196 memset( drive->label_conf + len, ' ', 11 - len );
197 drive->label_conf[11] = '\0';
200 /* Get the serial number */
201 PROFILE_GetWineIniString( name, "Serial", "12345678",
202 buffer, sizeof(buffer) );
203 drive->serial_conf = strtoul( buffer, NULL, 16 );
205 /* Get the filesystem type */
206 PROFILE_GetWineIniString( name, "Filesystem", "win95",
207 buffer, sizeof(buffer) );
208 drive->flags = DRIVE_GetFSFlags( name, buffer );
210 /* Get the device */
211 PROFILE_GetWineIniString( name, "Device", "",
212 buffer, sizeof(buffer) );
213 if (buffer[0])
215 drive->device = HEAP_strdupA( GetProcessHeap(), 0, buffer );
216 if (PROFILE_GetWineIniBool( name, "ReadVolInfo", 1))
217 drive->flags |= DRIVE_READ_VOL_INFO;
220 /* Get the FailReadOnly flag */
221 if (PROFILE_GetWineIniBool( name, "FailReadOnly", 0 ))
222 drive->flags |= DRIVE_FAIL_READ_ONLY;
224 /* Make the first hard disk the current drive */
225 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
226 DRIVE_CurDrive = i;
228 count++;
229 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
230 "flags=%08x dev=%x ino=%x\n",
231 name, path, DRIVE_Types[drive->type],
232 drive->label_conf, drive->serial_conf, drive->flags,
233 (int)drive->dev, (int)drive->ino );
235 else WARN("%s: not defined\n", name );
238 if (!count)
240 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
241 /* Create a C drive pointing to Unix root dir */
242 DOSDrives[2].root = HEAP_strdupA( GetProcessHeap(), 0, "/" );
243 DOSDrives[2].dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
244 DOSDrives[2].unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
245 strcpy( DOSDrives[2].label_conf, "Drive C " );
246 DOSDrives[2].serial_conf = 12345678;
247 DOSDrives[2].type = DRIVE_FIXED;
248 DOSDrives[2].device = NULL;
249 DOSDrives[2].flags = 0;
250 DRIVE_CurDrive = 2;
253 /* Make sure the current drive is valid */
254 if (DRIVE_CurDrive == -1)
256 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
258 if (drive->root && !(drive->flags & DRIVE_DISABLED))
260 DRIVE_CurDrive = i;
261 break;
266 /* get current working directory info for all drives */
267 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
269 if (!GetEnvironmentVariableA(drive_env, path, sizeof(path))) continue;
270 /* sanity check */
271 if (toupper(path[0]) != drive_env[1] || path[1] != ':') continue;
272 DRIVE_Chdir( i, path + 2 );
274 return 1;
278 /***********************************************************************
279 * DRIVE_IsValid
281 int DRIVE_IsValid( int drive )
283 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
284 return (DOSDrives[drive].root &&
285 !(DOSDrives[drive].flags & DRIVE_DISABLED));
289 /***********************************************************************
290 * DRIVE_GetCurrentDrive
292 int DRIVE_GetCurrentDrive(void)
294 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
295 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
296 return DRIVE_CurDrive;
300 /***********************************************************************
301 * DRIVE_SetCurrentDrive
303 int DRIVE_SetCurrentDrive( int drive )
305 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
306 if (!DRIVE_IsValid( drive ))
308 SetLastError( ERROR_INVALID_DRIVE );
309 return 0;
311 TRACE("%c:\n", 'A' + drive );
312 DRIVE_CurDrive = drive;
313 if (pTask) pTask->curdrive = drive | 0x80;
314 chdir(DRIVE_GetUnixCwd(drive));
315 return 1;
319 /***********************************************************************
320 * DRIVE_FindDriveRoot
322 * Find a drive for which the root matches the beginning of the given path.
323 * This can be used to translate a Unix path into a drive + DOS path.
324 * Return value is the drive, or -1 on error. On success, path is modified
325 * to point to the beginning of the DOS path.
327 int DRIVE_FindDriveRoot( const char **path )
329 /* idea: check at all '/' positions.
330 * If the device and inode of that path is identical with the
331 * device and inode of the current drive then we found a solution.
332 * If there is another drive pointing to a deeper position in
333 * the file tree, we want to find that one, not the earlier solution.
335 int drive, rootdrive = -1;
336 char buffer[MAX_PATHNAME_LEN];
337 char *next = buffer;
338 const char *p = *path;
339 struct stat st;
341 strcpy( buffer, "/" );
342 for (;;)
344 if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
346 /* Find the drive */
348 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
350 if (!DOSDrives[drive].root ||
351 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
353 if ((DOSDrives[drive].dev == st.st_dev) &&
354 (DOSDrives[drive].ino == st.st_ino))
356 rootdrive = drive;
357 *path = p;
358 break;
362 /* Get the next path component */
364 *next++ = '/';
365 while ((*p == '/') || (*p == '\\')) p++;
366 if (!*p) break;
367 while (!IS_END_OF_NAME(*p)) *next++ = *p++;
368 *next = 0;
370 *next = 0;
372 if (rootdrive != -1)
373 TRACE("%s -> drive %c:, root='%s', name='%s'\n",
374 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, *path );
375 return rootdrive;
379 /***********************************************************************
380 * DRIVE_GetRoot
382 const char * DRIVE_GetRoot( int drive )
384 if (!DRIVE_IsValid( drive )) return NULL;
385 return DOSDrives[drive].root;
389 /***********************************************************************
390 * DRIVE_GetDosCwd
392 const char * DRIVE_GetDosCwd( int drive )
394 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
395 if (!DRIVE_IsValid( drive )) return NULL;
397 /* Check if we need to change the directory to the new task. */
398 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
399 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
400 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
402 /* Perform the task-switch */
403 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
404 DRIVE_LastTask = GetCurrentTask();
406 return DOSDrives[drive].dos_cwd;
410 /***********************************************************************
411 * DRIVE_GetUnixCwd
413 const char * DRIVE_GetUnixCwd( int drive )
415 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
416 if (!DRIVE_IsValid( drive )) return NULL;
418 /* Check if we need to change the directory to the new task. */
419 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
420 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
421 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
423 /* Perform the task-switch */
424 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
425 DRIVE_LastTask = GetCurrentTask();
427 return DOSDrives[drive].unix_cwd;
431 /***********************************************************************
432 * DRIVE_GetDevice
434 const char * DRIVE_GetDevice( int drive )
436 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
440 /***********************************************************************
441 * DRIVE_ReadSuperblock
443 * NOTE
444 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
445 * to check, that they are writing on a FAT filesystem !
447 int DRIVE_ReadSuperblock (int drive, char * buff)
449 #define DRIVE_SUPER 96
450 int fd;
451 off_t offs;
453 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
454 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
456 struct stat st;
457 if (!DOSDrives[drive].device)
458 ERR("No device configured for drive %c: !\n", 'A'+drive);
459 else
460 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
461 (stat(DOSDrives[drive].device, &st)) ?
462 "not available or symlink not valid ?" : "no permission");
463 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
464 PROFILE_UsageWineIni();
465 return -1;
468 switch(DOSDrives[drive].type)
470 case DRIVE_REMOVABLE:
471 case DRIVE_FIXED:
472 offs = 0;
473 break;
474 case DRIVE_CDROM:
475 offs = CDROM_Data_FindBestVoldesc(fd);
476 break;
477 default:
478 offs = 0;
479 break;
482 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs)) return -4;
483 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER) return -2;
485 switch(DOSDrives[drive].type)
487 case DRIVE_REMOVABLE:
488 case DRIVE_FIXED:
489 if ((buff[0x26]!=0x29) || /* Check for FAT present */
490 /* FIXME: do really all FAT have their name beginning with
491 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
492 memcmp( buff+0x36,"FAT",3))
494 ERR("The filesystem is not FAT !! (device=%s)\n",
495 DOSDrives[drive].device);
496 return -3;
498 break;
499 case DRIVE_CDROM:
500 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
501 return -3;
502 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
503 break;
504 default:
505 return -3;
506 break;
509 return close(fd);
513 /***********************************************************************
514 * DRIVE_WriteSuperblockEntry
516 * NOTE
517 * We are writing as little as possible (ie. not the whole SuperBlock)
518 * not to interfere with kernel. The drive can be mounted !
520 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
522 int fd;
524 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
526 ERR("Cannot open the device %s (for writing)\n",
527 DOSDrives[drive].device);
528 return -1;
530 if (lseek(fd,ofs,SEEK_SET)!=ofs)
532 ERR("lseek failed on device %s !\n",
533 DOSDrives[drive].device);
534 close(fd);
535 return -2;
537 if (write(fd,buff,len)!=len)
539 close(fd);
540 ERR("Cannot write on %s !\n",
541 DOSDrives[drive].device);
542 return -3;
544 return close (fd);
549 /***********************************************************************
550 * DRIVE_GetLabel
552 const char * DRIVE_GetLabel( int drive )
554 int read = 0;
555 char buff[DRIVE_SUPER];
556 int offs = -1;
558 if (!DRIVE_IsValid( drive )) return NULL;
559 if (DOSDrives[drive].type == DRIVE_CDROM)
561 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
563 else
564 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
566 if (DRIVE_ReadSuperblock(drive,(char *) buff))
567 ERR("Invalid or unreadable superblock on %s (%c:).\n",
568 DOSDrives[drive].device, (char)(drive+'A'));
569 else {
570 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
571 DOSDrives[drive].type == DRIVE_FIXED)
572 offs = 0x2b;
574 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
575 if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
576 DOSDrives[drive].label_read[11]='\0';
577 read = 1;
581 return (read) ?
582 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
586 /***********************************************************************
587 * DRIVE_GetSerialNumber
589 DWORD DRIVE_GetSerialNumber( int drive )
591 DWORD serial = 0;
592 char buff[DRIVE_SUPER];
594 if (!DRIVE_IsValid( drive )) return 0;
596 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
598 switch(DOSDrives[drive].type)
600 case DRIVE_REMOVABLE:
601 case DRIVE_FIXED:
602 if (DRIVE_ReadSuperblock(drive,(char *) buff))
603 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
604 " Maybe not FAT?\n" ,
605 DOSDrives[drive].device, 'A'+drive);
606 else
607 serial = *((DWORD*)(buff+0x27));
608 break;
609 case DRIVE_CDROM:
610 serial = CDROM_GetSerial(drive);
611 break;
612 default:
613 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
617 return (serial) ? serial : DOSDrives[drive].serial_conf;
621 /***********************************************************************
622 * DRIVE_SetSerialNumber
624 int DRIVE_SetSerialNumber( int drive, DWORD serial )
626 char buff[DRIVE_SUPER];
628 if (!DRIVE_IsValid( drive )) return 0;
630 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
632 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
633 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
634 /* check, if the drive has a FAT filesystem */
635 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
636 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
637 return 1;
640 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
641 DOSDrives[drive].serial_conf = serial;
642 return 1;
646 /***********************************************************************
647 * DRIVE_GetType
649 static UINT DRIVE_GetType( int drive )
651 if (!DRIVE_IsValid( drive )) return DRIVE_UNKNOWN;
652 return DOSDrives[drive].type;
656 /***********************************************************************
657 * DRIVE_GetFlags
659 UINT DRIVE_GetFlags( int drive )
661 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
662 return DOSDrives[drive].flags;
666 /***********************************************************************
667 * DRIVE_Chdir
669 int DRIVE_Chdir( int drive, const char *path )
671 DOS_FULL_NAME full_name;
672 char buffer[MAX_PATHNAME_LEN];
673 LPSTR unix_cwd;
674 BY_HANDLE_FILE_INFORMATION info;
675 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
677 strcpy( buffer, "A:" );
678 buffer[0] += drive;
679 TRACE("(%s,%s)\n", buffer, path );
680 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
682 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
683 if (!FILE_Stat( full_name.long_name, &info )) return 0;
684 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
686 SetLastError( ERROR_FILE_NOT_FOUND );
687 return 0;
689 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
690 while (*unix_cwd == '/') unix_cwd++;
692 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
693 'A' + drive, unix_cwd, full_name.short_name + 3 );
695 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
696 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
697 DOSDrives[drive].dos_cwd = HEAP_strdupA( GetProcessHeap(), 0,
698 full_name.short_name + 3 );
699 DOSDrives[drive].unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, unix_cwd );
701 if (pTask && (pTask->curdrive & 0x80) &&
702 ((pTask->curdrive & ~0x80) == drive))
704 lstrcpynA( pTask->curdir, full_name.short_name + 2,
705 sizeof(pTask->curdir) );
706 DRIVE_LastTask = GetCurrentTask();
707 chdir(unix_cwd); /* Only change if on current drive */
709 return 1;
713 /***********************************************************************
714 * DRIVE_Disable
716 int DRIVE_Disable( int drive )
718 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
720 SetLastError( ERROR_INVALID_DRIVE );
721 return 0;
723 DOSDrives[drive].flags |= DRIVE_DISABLED;
724 return 1;
728 /***********************************************************************
729 * DRIVE_Enable
731 int DRIVE_Enable( int drive )
733 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
735 SetLastError( ERROR_INVALID_DRIVE );
736 return 0;
738 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
739 return 1;
743 /***********************************************************************
744 * DRIVE_SetLogicalMapping
746 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
748 /* If new_drive is already valid, do nothing and return 0
749 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
751 DOSDRIVE *old, *new;
753 old = DOSDrives + existing_drive;
754 new = DOSDrives + new_drive;
756 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
757 !old->root ||
758 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
760 SetLastError( ERROR_INVALID_DRIVE );
761 return 0;
764 if ( new->root )
766 TRACE("Can't map drive %c: to already existing drive %c:\n",
767 'A' + existing_drive, 'A' + new_drive );
768 /* it is already mapped there, so return success */
769 if (!strcmp(old->root,new->root))
770 return 1;
771 return 0;
774 new->root = HEAP_strdupA( GetProcessHeap(), 0, old->root );
775 new->dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->dos_cwd );
776 new->unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->unix_cwd );
777 new->device = HEAP_strdupA( GetProcessHeap(), 0, old->device );
778 memcpy ( new->label_conf, old->label_conf, 12 );
779 memcpy ( new->label_read, old->label_read, 12 );
780 new->serial_conf = old->serial_conf;
781 new->type = old->type;
782 new->flags = old->flags;
783 new->dev = old->dev;
784 new->ino = old->ino;
786 TRACE("Drive %c: is now equal to drive %c:\n",
787 'A' + new_drive, 'A' + existing_drive );
789 return 1;
793 /***********************************************************************
794 * DRIVE_OpenDevice
796 * Open the drive raw device and return a Unix fd (or -1 on error).
798 int DRIVE_OpenDevice( int drive, int flags )
800 if (!DRIVE_IsValid( drive )) return -1;
801 return open( DOSDrives[drive].device, flags );
805 /***********************************************************************
806 * DRIVE_RawRead
808 * Read raw sectors from a device
810 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
812 int fd;
814 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
816 lseek( fd, begin * 512, SEEK_SET );
817 /* FIXME: check errors */
818 read( fd, dataptr, nr_sect * 512 );
819 close( fd );
821 else
823 memset(dataptr, 0, nr_sect * 512);
824 if (fake_success)
826 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
827 if (begin == 1) *dataptr = 0xf8;
829 else
830 return 0;
832 return 1;
836 /***********************************************************************
837 * DRIVE_RawWrite
839 * Write raw sectors to a device
841 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
843 int fd;
845 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
847 lseek( fd, begin * 512, SEEK_SET );
848 /* FIXME: check errors */
849 write( fd, dataptr, nr_sect * 512 );
850 close( fd );
852 else
853 if (!(fake_success))
854 return 0;
856 return 1;
860 /***********************************************************************
861 * DRIVE_GetFreeSpace
863 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
864 PULARGE_INTEGER available )
866 struct statfs info;
868 if (!DRIVE_IsValid(drive))
870 SetLastError( ERROR_INVALID_DRIVE );
871 return 0;
874 /* FIXME: add autoconf check for this */
875 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
876 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
877 #else
878 if (statfs( DOSDrives[drive].root, &info) < 0)
879 #endif
881 FILE_SetDosError();
882 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
883 return 0;
886 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
887 #ifdef STATFS_HAS_BAVAIL
888 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
889 #else
890 # ifdef STATFS_HAS_BFREE
891 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
892 # else
893 # error "statfs has no bfree/bavail member!"
894 # endif
895 #endif
896 if (DOSDrives[drive].type == DRIVE_CDROM)
897 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
898 available->QuadPart = 0;
900 return 1;
903 /***********************************************************************
904 * DRIVE_GetCurrentDirectory
905 * Returns "X:\\path\\etc\\".
907 * Despite the API description, return required length including the
908 * terminating null when buffer too small. This is the real behaviour.
911 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
913 UINT ret;
914 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
916 assert(s);
917 ret = strlen(s) + 3; /* length of WHOLE current directory */
918 if (ret >= buflen) return ret + 1;
919 lstrcpynA( buf, "A:\\", min( 4, buflen ) );
920 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
921 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
922 return ret;
926 /***********************************************************************
927 * DRIVE_BuildEnv
929 * Build the environment array containing the drives current directories.
930 * Resulting pointer must be freed with HeapFree.
932 char *DRIVE_BuildEnv(void)
934 int i, length = 0;
935 const char *cwd[MAX_DOS_DRIVES];
936 char *env, *p;
938 for (i = 0; i < MAX_DOS_DRIVES; i++)
940 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0]) length += strlen(cwd[i]) + 8;
942 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
943 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
945 if (cwd[i] && cwd[i][0])
946 p += sprintf( p, "=%c:=%c:\\%s", 'A'+i, 'A'+i, cwd[i] ) + 1;
948 *p = 0;
949 return env;
953 /***********************************************************************
954 * GetDiskFreeSpace16 (KERNEL.422)
956 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
957 LPDWORD sector_bytes, LPDWORD free_clusters,
958 LPDWORD total_clusters )
960 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
961 free_clusters, total_clusters );
965 /***********************************************************************
966 * GetDiskFreeSpaceA (KERNEL32.206)
968 * Fails if expression resulting from current drive's dir and "root"
969 * is not a root dir of the target drive.
971 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
972 * if the corresponding info is unneeded.
974 * FIXME: needs to support UNC names from Win95 OSR2 on.
976 * Behaviour under Win95a:
977 * CurrDir root result
978 * "E:\\TEST" "E:" FALSE
979 * "E:\\" "E:" TRUE
980 * "E:\\" "E" FALSE
981 * "E:\\" "\\" TRUE
982 * "E:\\TEST" "\\" TRUE
983 * "E:\\TEST" ":\\" FALSE
984 * "E:\\TEST" "E:\\" TRUE
985 * "E:\\TEST" "" FALSE
986 * "E:\\" "" FALSE (!)
987 * "E:\\" 0x0 TRUE
988 * "E:\\TEST" 0x0 TRUE (!)
989 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
990 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
992 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
993 LPDWORD sector_bytes, LPDWORD free_clusters,
994 LPDWORD total_clusters )
996 int drive, sec_size;
997 ULARGE_INTEGER size,available;
998 LPCSTR path;
999 DWORD cluster_sec;
1001 if ((!root) || (strcmp(root,"\\") == 0))
1002 drive = DRIVE_GetCurrentDrive();
1003 else
1004 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
1006 drive = toupper(root[0]) - 'A';
1007 path = &root[2];
1008 if (path[0] == '\0')
1009 path = DRIVE_GetDosCwd(drive);
1010 else
1011 if (path[0] == '\\')
1012 path++;
1013 if (strlen(path)) /* oops, we are in a subdir */
1014 return FALSE;
1016 else
1017 return FALSE;
1019 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1021 /* Cap the size and available at 2GB as per specs. */
1022 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1024 size.s.HighPart = 0;
1025 size.s.LowPart = 0x7fffffff;
1027 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1029 available.s.HighPart =0;
1030 available.s.LowPart = 0x7fffffff;
1032 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1033 size.s.LowPart /= sec_size;
1034 available.s.LowPart /= sec_size;
1035 /* fixme: probably have to adjust those variables too for CDFS */
1036 cluster_sec = 1;
1037 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1039 if (cluster_sectors)
1040 *cluster_sectors = cluster_sec;
1041 if (sector_bytes)
1042 *sector_bytes = sec_size;
1043 if (free_clusters)
1044 *free_clusters = available.s.LowPart / cluster_sec;
1045 if (total_clusters)
1046 *total_clusters = size.s.LowPart / cluster_sec;
1047 return TRUE;
1051 /***********************************************************************
1052 * GetDiskFreeSpaceW (KERNEL32.207)
1054 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1055 LPDWORD sector_bytes, LPDWORD free_clusters,
1056 LPDWORD total_clusters )
1058 LPSTR xroot;
1059 BOOL ret;
1061 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1062 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
1063 free_clusters, total_clusters );
1064 HeapFree( GetProcessHeap(), 0, xroot );
1065 return ret;
1069 /***********************************************************************
1070 * GetDiskFreeSpaceExA (KERNEL32.871)
1072 * This function is used to aquire the size of the available and
1073 * total space on a logical volume.
1075 * RETURNS
1077 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1078 * detailed error information.
1081 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1082 PULARGE_INTEGER avail,
1083 PULARGE_INTEGER total,
1084 PULARGE_INTEGER totalfree)
1086 int drive;
1087 ULARGE_INTEGER size,available;
1089 if (!root) drive = DRIVE_GetCurrentDrive();
1090 else
1092 if ((root[1]) && ((root[1] != ':') || (root[2] != '\\')))
1094 FIXME("there are valid root names which are not supported yet\n");
1095 /* ..like UNC names, for instance. */
1097 WARN("invalid root '%s'\n", root );
1098 return FALSE;
1100 drive = toupper(root[0]) - 'A';
1103 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1105 if (total)
1107 total->s.HighPart = size.s.HighPart;
1108 total->s.LowPart = size.s.LowPart;
1111 if (totalfree)
1113 totalfree->s.HighPart = available.s.HighPart;
1114 totalfree->s.LowPart = available.s.LowPart;
1117 if (avail)
1119 if (FIXME_ON(dosfs))
1121 /* On Windows2000, we need to check the disk quota
1122 allocated for the user owning the calling process. We
1123 don't want to be more obtrusive than necessary with the
1124 FIXME messages, so don't print the FIXME unless Wine is
1125 actually masquerading as Windows2000. */
1127 OSVERSIONINFOA ovi;
1128 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1129 if (GetVersionExA(&ovi))
1131 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1132 FIXME("no per-user quota support yet\n");
1136 /* Quick hack, should eventually be fixed to work 100% with
1137 Windows2000 (see comment above). */
1138 avail->s.HighPart = available.s.HighPart;
1139 avail->s.LowPart = available.s.LowPart;
1142 return TRUE;
1145 /***********************************************************************
1146 * GetDiskFreeSpaceExW (KERNEL32.873)
1148 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1149 PULARGE_INTEGER total,
1150 PULARGE_INTEGER totalfree)
1152 LPSTR xroot;
1153 BOOL ret;
1155 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1156 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1157 HeapFree( GetProcessHeap(), 0, xroot );
1158 return ret;
1161 /***********************************************************************
1162 * GetDriveType16 (KERNEL.136)
1163 * This function returns the type of a drive in Win16.
1164 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1165 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1166 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1167 * do any pseudo-clever changes.
1169 * RETURNS
1170 * drivetype DRIVE_xxx
1172 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1174 UINT type = DRIVE_GetType(drive);
1175 TRACE("(%c:)\n", 'A' + drive );
1176 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1177 return type;
1181 /***********************************************************************
1182 * GetDriveTypeA (KERNEL32.208)
1184 * Returns the type of the disk drive specified. If root is NULL the
1185 * root of the current directory is used.
1187 * RETURNS
1189 * Type of drive (from Win32 SDK):
1191 * DRIVE_UNKNOWN unable to find out anything about the drive
1192 * DRIVE_NO_ROOT_DIR nonexistent root dir
1193 * DRIVE_REMOVABLE the disk can be removed from the machine
1194 * DRIVE_FIXED the disk can not be removed from the machine
1195 * DRIVE_REMOTE network disk
1196 * DRIVE_CDROM CDROM drive
1197 * DRIVE_RAMDISK virtual disk in RAM
1199 UINT WINAPI GetDriveTypeA(LPCSTR root) /* [in] String describing drive */
1201 int drive;
1202 TRACE("(%s)\n", debugstr_a(root));
1204 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1205 else
1207 if ((root[1]) && (root[1] != ':'))
1209 WARN("invalid root '%s'\n", debugstr_a(root));
1210 return DRIVE_NO_ROOT_DIR;
1212 drive = toupper(root[0]) - 'A';
1214 return DRIVE_GetType(drive);
1218 /***********************************************************************
1219 * GetDriveTypeW (KERNEL32.209)
1221 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1223 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1224 UINT ret = GetDriveTypeA( xpath );
1225 HeapFree( GetProcessHeap(), 0, xpath );
1226 return ret;
1230 /***********************************************************************
1231 * GetCurrentDirectory16 (KERNEL.411)
1233 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1235 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1239 /***********************************************************************
1240 * GetCurrentDirectoryA (KERNEL32.196)
1242 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1244 UINT ret;
1245 char longname[MAX_PATHNAME_LEN];
1246 char shortname[MAX_PATHNAME_LEN];
1247 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1248 if ( ret > MAX_PATHNAME_LEN ) {
1249 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1250 return ret;
1252 GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1253 ret = strlen( longname ) + 1;
1254 if (ret > buflen) return ret;
1255 strcpy(buf, longname);
1256 return ret - 1;
1259 /***********************************************************************
1260 * GetCurrentDirectoryW (KERNEL32.197)
1262 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1264 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1265 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1266 if (ret < buflen) ret = MultiByteToWideChar( CP_ACP, 0, xpath, -1, buf, buflen ) - 1;
1267 HeapFree( GetProcessHeap(), 0, xpath );
1268 return ret;
1272 /***********************************************************************
1273 * SetCurrentDirectory (KERNEL.412)
1275 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1277 return SetCurrentDirectoryA( dir );
1281 /***********************************************************************
1282 * SetCurrentDirectoryA (KERNEL32.479)
1284 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1286 int drive, olddrive = DRIVE_GetCurrentDrive();
1288 if (!dir) {
1289 ERR_(file)("(NULL)!\n");
1290 return FALSE;
1292 if (dir[0] && (dir[1]==':'))
1294 drive = toupper( *dir ) - 'A';
1295 dir += 2;
1297 else
1298 drive = olddrive;
1300 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1301 sets pTask->curdir only if pTask->curdrive is drive */
1302 if (!(DRIVE_SetCurrentDrive( drive )))
1303 return FALSE;
1304 /* FIXME: what about empty strings? Add a \\ ? */
1305 if (!DRIVE_Chdir( drive, dir )) {
1306 DRIVE_SetCurrentDrive(olddrive);
1307 return FALSE;
1309 return TRUE;
1313 /***********************************************************************
1314 * SetCurrentDirectoryW (KERNEL32.480)
1316 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1318 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1319 BOOL res = SetCurrentDirectoryA( dir );
1320 HeapFree( GetProcessHeap(), 0, dir );
1321 return res;
1325 /***********************************************************************
1326 * GetLogicalDriveStringsA (KERNEL32.231)
1328 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1330 int drive, count;
1332 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1333 if (DRIVE_IsValid(drive)) count++;
1334 if ((count * 4) + 1 <= len)
1336 LPSTR p = buffer;
1337 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1338 if (DRIVE_IsValid(drive))
1340 *p++ = 'a' + drive;
1341 *p++ = ':';
1342 *p++ = '\\';
1343 *p++ = '\0';
1345 *p = '\0';
1346 return count * 4;
1348 else
1349 return (count * 4) + 1; /* account for terminating null */
1350 /* The API tells about these different return values */
1354 /***********************************************************************
1355 * GetLogicalDriveStringsW (KERNEL32.232)
1357 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1359 int drive, count;
1361 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1362 if (DRIVE_IsValid(drive)) count++;
1363 if (count * 4 * sizeof(WCHAR) <= len)
1365 LPWSTR p = buffer;
1366 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1367 if (DRIVE_IsValid(drive))
1369 *p++ = (WCHAR)('a' + drive);
1370 *p++ = (WCHAR)':';
1371 *p++ = (WCHAR)'\\';
1372 *p++ = (WCHAR)'\0';
1374 *p = (WCHAR)'\0';
1376 return count * 4 * sizeof(WCHAR);
1380 /***********************************************************************
1381 * GetLogicalDrives (KERNEL32.233)
1383 DWORD WINAPI GetLogicalDrives(void)
1385 DWORD ret = 0;
1386 int drive;
1388 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1390 if ( (DRIVE_IsValid(drive)) ||
1391 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1392 ret |= (1 << drive);
1394 return ret;
1398 /***********************************************************************
1399 * GetVolumeInformationA (KERNEL32.309)
1401 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1402 DWORD label_len, DWORD *serial,
1403 DWORD *filename_len, DWORD *flags,
1404 LPSTR fsname, DWORD fsname_len )
1406 int drive;
1407 char *cp;
1409 /* FIXME, SetLastError()s missing */
1411 if (!root) drive = DRIVE_GetCurrentDrive();
1412 else
1414 if ((root[1]) && (root[1] != ':'))
1416 WARN("invalid root '%s'\n",root);
1417 return FALSE;
1419 drive = toupper(root[0]) - 'A';
1421 if (!DRIVE_IsValid( drive )) return FALSE;
1422 if (label)
1424 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1425 for (cp = label; *cp; cp++);
1426 while (cp != label && *(cp-1) == ' ') cp--;
1427 *cp = '\0';
1429 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1431 /* Set the filesystem information */
1432 /* Note: we only emulate a FAT fs at present */
1434 if (filename_len) {
1435 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1436 *filename_len = 12;
1437 else
1438 *filename_len = 255;
1440 if (flags)
1442 *flags=0;
1443 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1444 *flags|=FS_CASE_SENSITIVE;
1445 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1446 *flags|=FS_CASE_IS_PRESERVED;
1448 if (fsname) {
1449 /* Diablo checks that return code ... */
1450 if (DOSDrives[drive].type == DRIVE_CDROM)
1451 lstrcpynA( fsname, "CDFS", fsname_len );
1452 else
1453 lstrcpynA( fsname, "FAT", fsname_len );
1455 return TRUE;
1459 /***********************************************************************
1460 * GetVolumeInformationW (KERNEL32.310)
1462 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1463 DWORD label_len, DWORD *serial,
1464 DWORD *filename_len, DWORD *flags,
1465 LPWSTR fsname, DWORD fsname_len )
1467 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1468 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1469 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1470 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1471 filename_len, flags, xfsname,
1472 fsname_len );
1473 if (ret)
1475 if (label) MultiByteToWideChar( CP_ACP, 0, xvolname, -1, label, label_len );
1476 if (fsname) MultiByteToWideChar( CP_ACP, 0, xfsname, -1, fsname, fsname_len );
1478 HeapFree( GetProcessHeap(), 0, xroot );
1479 HeapFree( GetProcessHeap(), 0, xvolname );
1480 HeapFree( GetProcessHeap(), 0, xfsname );
1481 return ret;
1484 /***********************************************************************
1485 * SetVolumeLabelA (KERNEL32.675)
1487 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1489 int drive;
1491 /* FIXME, SetLastErrors missing */
1493 if (!root) drive = DRIVE_GetCurrentDrive();
1494 else
1496 if ((root[1]) && (root[1] != ':'))
1498 WARN("invalid root '%s'\n",root);
1499 return FALSE;
1501 drive = toupper(root[0]) - 'A';
1503 if (!DRIVE_IsValid( drive )) return FALSE;
1505 /* some copy protection stuff check this */
1506 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
1508 FIXME("(%s,%s),stub!\n", root, volname);
1509 return TRUE;
1512 /***********************************************************************
1513 * SetVolumeLabelW (KERNEL32.676)
1515 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1517 LPSTR xroot, xvol;
1518 BOOL ret;
1520 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1521 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1522 ret = SetVolumeLabelA( xroot, xvol );
1523 HeapFree( GetProcessHeap(), 0, xroot );
1524 HeapFree( GetProcessHeap(), 0, xvol );
1525 return ret;