Move definition of CPINFO structure from winbase.h to winnls.h
[wine] / files / drive.c
1 /*
2  * DOS drives handling functions
3  *
4  * Copyright 1993 Erik Bos
5  * Copyright 1996 Alexandre Julliard
6  *
7  * Label & serial number read support.
8  *  (c) 1999 Petr Tomasek <tomasek@etf.cuni.cz>
9  *  (c) 2000 Andreas Mohr (changes)
10  *
11  */
12
13 #include "config.h"
14
15 #include <assert.h>
16 #include <ctype.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <unistd.h>
24
25 #ifdef HAVE_SYS_PARAM_H
26 # include <sys/param.h>
27 #endif
28 #ifdef STATFS_DEFINED_BY_SYS_VFS
29 # include <sys/vfs.h>
30 #else
31 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
32 #  include <sys/mount.h>
33 # else
34 #  ifdef STATFS_DEFINED_BY_SYS_STATFS
35 #   include <sys/statfs.h>
36 #  endif
37 # endif
38 #endif
39
40 #include "winbase.h"
41 #include "wine/winbase16.h"   /* for GetCurrentTask */
42 #include "wine/winestring.h"  /* for lstrcpyAtoW */
43 #include "winerror.h"
44 #include "drive.h"
45 #include "cdrom.h"
46 #include "file.h"
47 #include "heap.h"
48 #include "msdos.h"
49 #include "options.h"
50 #include "wine/port.h"
51 #include "task.h"
52 #include "debugtools.h"
53
54 DEFAULT_DEBUG_CHANNEL(dosfs)
55 DECLARE_DEBUG_CHANNEL(file)
56
57 typedef struct
58 {
59     char     *root;      /* root dir in Unix format without trailing / */
60     char     *dos_cwd;   /* cwd in DOS format without leading or trailing \ */
61     char     *unix_cwd;  /* cwd in Unix format without leading or trailing / */
62     char     *device;    /* raw device path */
63     BOOL      read_volinfo; /* read the volume info from the device ? */
64     char      label_conf[12]; /* drive label as cfg'd in wine.conf */
65     char      label_read[12]; /* drive label as read from device */
66     DWORD     serial_conf;    /* drive serial number as cfg'd in wine.conf */
67     DRIVETYPE type;      /* drive type */
68     UINT      flags;     /* drive flags */
69     dev_t     dev;       /* unix device number */
70     ino_t     ino;       /* unix inode number */
71 } DOSDRIVE;
72
73
74 static const char * const DRIVE_Types[] =
75 {
76     "floppy",   /* TYPE_FLOPPY */
77     "hd",       /* TYPE_HD */
78     "cdrom",    /* TYPE_CDROM */
79     "network"   /* TYPE_NETWORK */
80 };
81
82
83 /* Known filesystem types */
84
85 typedef struct
86 {
87     const char *name;
88     UINT      flags;
89 } FS_DESCR;
90
91 static const FS_DESCR DRIVE_Filesystems[] =
92 {
93     { "unix",   DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
94     { "msdos",  DRIVE_SHORT_NAMES },
95     { "dos",    DRIVE_SHORT_NAMES },
96     { "fat",    DRIVE_SHORT_NAMES },
97     { "vfat",   DRIVE_CASE_PRESERVING },
98     { "win95",  DRIVE_CASE_PRESERVING },
99     { NULL, 0 }
100 };
101
102
103 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
104 static int DRIVE_CurDrive = -1;
105
106 static HTASK16 DRIVE_LastTask = 0;
107
108
109 /***********************************************************************
110  *           DRIVE_GetDriveType
111  */
112 static DRIVETYPE DRIVE_GetDriveType( const char *name )
113 {
114     char buffer[20];
115     int i;
116
117     PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
118     for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
119     {
120         if (!strcasecmp( buffer, DRIVE_Types[i] )) return (DRIVETYPE)i;
121     }
122     MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
123         name, buffer );
124     return TYPE_HD;
125 }
126
127
128 /***********************************************************************
129  *           DRIVE_GetFSFlags
130  */
131 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
132 {
133     const FS_DESCR *descr;
134
135     for (descr = DRIVE_Filesystems; descr->name; descr++)
136         if (!strcasecmp( value, descr->name )) return descr->flags;
137     MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
138         name, value );
139     return DRIVE_CASE_PRESERVING;
140 }
141
142
143 /***********************************************************************
144  *           DRIVE_Init
145  */
146 int DRIVE_Init(void)
147 {
148     int i, len, count = 0;
149     char name[] = "Drive A";
150     char path[MAX_PATHNAME_LEN];
151     char buffer[80];
152     struct stat drive_stat_buffer;
153     char *p;
154     DOSDRIVE *drive;
155
156     for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
157     {
158         PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
159         if (path[0])
160         {
161             p = path + strlen(path) - 1;
162             while ((p > path) && ((*p == '/') || (*p == '\\'))) *p-- = '\0';
163             if (!path[0]) strcpy( path, "/" );
164
165             if (stat( path, &drive_stat_buffer ))
166             {
167                 MESSAGE("Could not stat %s, ignoring drive %c:\n", path, 'A' + i );
168                 continue;
169             }
170             if (!S_ISDIR(drive_stat_buffer.st_mode))
171             {
172                 MESSAGE("%s is not a directory, ignoring drive %c:\n",
173                     path, 'A' + i );
174                 continue;
175             }
176
177             drive->root = HEAP_strdupA( GetProcessHeap(), 0, path );
178             drive->dos_cwd  = HEAP_strdupA( GetProcessHeap(), 0, "" );
179             drive->unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
180             drive->type     = DRIVE_GetDriveType( name );
181             drive->device   = NULL;
182             drive->flags    = 0;
183             drive->dev      = drive_stat_buffer.st_dev;
184             drive->ino      = drive_stat_buffer.st_ino;
185
186             /* Get the drive label */
187             PROFILE_GetWineIniString( name, "Label", name, drive->label_conf, 12 );
188             if ((len = strlen(drive->label_conf)) < 11)
189             {
190                 /* Pad label with spaces */
191                 memset( drive->label_conf + len, ' ', 11 - len );
192                 drive->label_conf[11] = '\0';
193             }
194
195             /* Get the serial number */
196             PROFILE_GetWineIniString( name, "Serial", "12345678",
197                                       buffer, sizeof(buffer) );
198             drive->serial_conf = strtoul( buffer, NULL, 16 );
199
200             /* Get the filesystem type */
201             PROFILE_GetWineIniString( name, "Filesystem", "win95",
202                                       buffer, sizeof(buffer) );
203             drive->flags = DRIVE_GetFSFlags( name, buffer );
204
205             /* Get the device */
206             PROFILE_GetWineIniString( name, "Device", "",
207                                       buffer, sizeof(buffer) );
208             if (buffer[0])
209             {
210                 drive->device = HEAP_strdupA( GetProcessHeap(), 0, buffer );
211                 drive->read_volinfo =
212                 (BOOL)PROFILE_GetWineIniInt( name, "ReadVolInfo", 1);
213             }
214             else
215                 drive->read_volinfo = FALSE;
216
217             /* Make the first hard disk the current drive */
218             if ((DRIVE_CurDrive == -1) && (drive->type == TYPE_HD))
219                 DRIVE_CurDrive = i;
220
221             count++;
222             TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
223                   "flags=%08x dev=%x ino=%x\n",
224                   name, path, DRIVE_Types[drive->type],
225                   drive->label_conf, drive->serial_conf, drive->flags,
226                   (int)drive->dev, (int)drive->ino );
227         }
228         else WARN("%s: not defined\n", name );
229     }
230
231     if (!count) 
232     {
233         MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
234         /* Create a C drive pointing to Unix root dir */
235         DOSDrives[2].root     = HEAP_strdupA( GetProcessHeap(), 0, "/" );
236         DOSDrives[2].dos_cwd  = HEAP_strdupA( GetProcessHeap(), 0, "" );
237         DOSDrives[2].unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
238         strcpy( DOSDrives[2].label_conf, "Drive C    " );
239         DOSDrives[2].serial_conf   = 12345678;
240         DOSDrives[2].type     = TYPE_HD;
241         DOSDrives[2].flags    = 0;
242         DRIVE_CurDrive = 2;
243     }
244
245     /* Make sure the current drive is valid */
246     if (DRIVE_CurDrive == -1)
247     {
248         for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
249         {
250             if (drive->root && !(drive->flags & DRIVE_DISABLED))
251             {
252                 DRIVE_CurDrive = i;
253                 break;
254             }
255         }
256     }
257
258     return 1;
259 }
260
261
262 /***********************************************************************
263  *           DRIVE_IsValid
264  */
265 int DRIVE_IsValid( int drive )
266 {
267     if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
268     return (DOSDrives[drive].root &&
269             !(DOSDrives[drive].flags & DRIVE_DISABLED));
270 }
271
272
273 /***********************************************************************
274  *           DRIVE_GetCurrentDrive
275  */
276 int DRIVE_GetCurrentDrive(void)
277 {
278     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
279     if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
280     return DRIVE_CurDrive;
281 }
282
283
284 /***********************************************************************
285  *           DRIVE_SetCurrentDrive
286  */
287 int DRIVE_SetCurrentDrive( int drive )
288 {
289     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
290     if (!DRIVE_IsValid( drive ))
291     {
292         SetLastError( ERROR_INVALID_DRIVE );
293         return 0;
294     }
295     TRACE("%c:\n", 'A' + drive );
296     DRIVE_CurDrive = drive;
297     if (pTask) pTask->curdrive = drive | 0x80;
298     return 1;
299 }
300
301
302 /***********************************************************************
303  *           DRIVE_FindDriveRoot
304  *
305  * Find a drive for which the root matches the beginning of the given path.
306  * This can be used to translate a Unix path into a drive + DOS path.
307  * Return value is the drive, or -1 on error. On success, path is modified
308  * to point to the beginning of the DOS path.
309  */
310 int DRIVE_FindDriveRoot( const char **path )
311 {
312     /* idea: check at all '/' positions.
313      * If the device and inode of that path is identical with the
314      * device and inode of the current drive then we found a solution.
315      * If there is another drive pointing to a deeper position in
316      * the file tree, we want to find that one, not the earlier solution.
317      */
318     int drive, rootdrive = -1;
319     char buffer[MAX_PATHNAME_LEN];
320     char *next = buffer;
321     const char *p = *path;
322     struct stat st;
323
324     strcpy( buffer, "/" );
325     for (;;)
326     {
327         if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
328
329         /* Find the drive */
330
331         for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
332         {
333            if (!DOSDrives[drive].root ||
334                (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
335
336            if ((DOSDrives[drive].dev == st.st_dev) &&
337                (DOSDrives[drive].ino == st.st_ino))
338            {
339                rootdrive = drive;
340                *path = p;
341                break;
342            }
343         }
344
345         /* Get the next path component */
346
347         *next++ = '/';
348         while ((*p == '/') || (*p == '\\')) p++;
349         if (!*p) break;
350         while (!IS_END_OF_NAME(*p)) *next++ = *p++;
351         *next = 0;
352     }
353     *next = 0;
354
355     if (rootdrive != -1)
356         TRACE("%s -> drive %c:, root='%s', name='%s'\n",
357               buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, *path );
358     return rootdrive;
359 }
360
361
362 /***********************************************************************
363  *           DRIVE_GetRoot
364  */
365 const char * DRIVE_GetRoot( int drive )
366 {
367     if (!DRIVE_IsValid( drive )) return NULL;
368     return DOSDrives[drive].root;
369 }
370
371
372 /***********************************************************************
373  *           DRIVE_GetDosCwd
374  */
375 const char * DRIVE_GetDosCwd( int drive )
376 {
377     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
378     if (!DRIVE_IsValid( drive )) return NULL;
379
380     /* Check if we need to change the directory to the new task. */
381     if (pTask && (pTask->curdrive & 0x80) &&    /* The task drive is valid */
382         ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
383         (DRIVE_LastTask != GetCurrentTask()))   /* and the task changed */
384     {
385         /* Perform the task-switch */
386         if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
387         DRIVE_LastTask = GetCurrentTask();
388     }
389     return DOSDrives[drive].dos_cwd;
390 }
391
392
393 /***********************************************************************
394  *           DRIVE_GetUnixCwd
395  */
396 const char * DRIVE_GetUnixCwd( int drive )
397 {
398     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
399     if (!DRIVE_IsValid( drive )) return NULL;
400
401     /* Check if we need to change the directory to the new task. */
402     if (pTask && (pTask->curdrive & 0x80) &&    /* The task drive is valid */
403         ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
404         (DRIVE_LastTask != GetCurrentTask()))   /* and the task changed */
405     {
406         /* Perform the task-switch */
407         if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
408         DRIVE_LastTask = GetCurrentTask();
409     }
410     return DOSDrives[drive].unix_cwd;
411 }
412
413
414 /***********************************************************************
415  *           DRIVE_GetDevice
416  */
417 const char * DRIVE_GetDevice( int drive )
418 {
419     return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
420 }
421
422
423 /***********************************************************************
424  *           DRIVE_ReadSuperblock
425  *
426  * Used in DRIVE_GetLabel
427  */
428 int DRIVE_ReadSuperblock (int drive, char * buff)
429 {
430 #define DRIVE_SUPER 96
431     int fd;
432     off_t offs;
433
434     if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
435     if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
436     {
437         struct stat st;
438         if (!DOSDrives[drive].device)
439             ERR("No device configured for drive %c: !\n", 'A'+drive);
440         else
441             ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
442                  (stat(DOSDrives[drive].device, &st)) ?
443                         "not available or symlink not valid ?" : "no permission");
444         ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
445         PROFILE_UsageWineIni();
446         return -1;
447     }
448
449     switch(DOSDrives[drive].type)
450     {
451         case TYPE_FLOPPY:
452         case TYPE_HD:
453             offs = 0;
454             break;
455         case TYPE_CDROM:
456         /* FIXME: Maybe we should search for the first data track on the CD,
457                   not just assume that it is the first track */
458             offs = (off_t)2048*(16+0);
459             break;
460                 default:
461                     offs = 0;
462                     break;
463     }
464
465     if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs)) return -4;
466     if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER) return -2;
467
468     switch(DOSDrives[drive].type)
469     {
470         case TYPE_FLOPPY:
471         case TYPE_HD:
472             if (buff[0x26]!=0x29) /* Check for FAT present */
473                 return -3;
474                 break;
475         case TYPE_CDROM:
476             if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
477                 return -3;
478             /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
479                 break;
480         default:
481                 return -3;
482                 break;
483     }
484
485     return close(fd);
486 }
487
488
489 /***********************************************************************
490  *           DRIVE_GetLabel
491  */
492 const char * DRIVE_GetLabel( int drive )
493 {
494     int read = 0;
495     char buff[DRIVE_SUPER];
496     int offs = -1;
497
498     if (!DRIVE_IsValid( drive )) return NULL;
499     if (DRIVE_GetType(drive) == TYPE_CDROM)
500     {
501         WINE_CDAUDIO wcda;
502
503         if (!(CDROM_Open(&wcda, drive)))
504         {
505             int media = CDROM_GetMediaType(&wcda);
506
507             if (media == CDS_AUDIO)
508             {
509                 strcpy(DOSDrives[drive].label_read, "Audio CD   ");
510                 read = 1;
511             }
512             else
513             if (media == CDS_NO_INFO)
514             {
515                 strcpy(DOSDrives[drive].label_read, "           ");
516                 read = 1;
517             }
518
519             CDROM_Close(&wcda);
520 }
521     }
522     if ((!read) && (DOSDrives[drive].read_volinfo))
523     {
524         if (DRIVE_ReadSuperblock(drive,(char *) buff))
525             ERR("Invalid or unreadable superblock on %s (%c:).\n",
526                 DOSDrives[drive].device, (char)(drive+'A'));
527         else {
528             if (DOSDrives[drive].type == TYPE_CDROM)
529                 offs = 40;
530             else
531             if (DOSDrives[drive].type == TYPE_FLOPPY ||
532                 DOSDrives[drive].type == TYPE_HD)
533                 offs = 0x2b;
534
535             /* FIXME: ISO9660 uses 32-bytes long label. Should we do also? */
536             if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
537             DOSDrives[drive].label_read[11]='\0';
538             read = 1;
539         }
540     }
541
542     return (read) ?
543         DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
544 }
545
546
547 /***********************************************************************
548  *           DRIVE_GetSerialNumber
549  */
550 DWORD DRIVE_GetSerialNumber( int drive )
551 {
552     DWORD serial = 0;
553 char buff[DRIVE_SUPER];
554
555     if (!DRIVE_IsValid( drive )) return 0;
556     
557     if (DOSDrives[drive].read_volinfo)
558     {
559         switch(DOSDrives[drive].type)
560         {
561             case TYPE_FLOPPY:
562             case TYPE_HD:
563       if (DRIVE_ReadSuperblock(drive,(char *) buff))
564         MESSAGE("Invalid or unreadable superblock on %s (%c:)."
565                         " Maybe not FAT?\n" ,
566                         DOSDrives[drive].device, 'A'+drive);
567       else
568                     serial = *((DWORD*)(buff+0x27));
569                 break;
570             case TYPE_CDROM:
571                 serial = CDROM_GetSerial(drive);
572                 break;
573             default:
574                 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
575         }
576     }
577                 
578     return (serial) ? serial : DOSDrives[drive].serial_conf;
579 }
580
581
582 /***********************************************************************
583  *           DRIVE_SetSerialNumber
584  */
585 int DRIVE_SetSerialNumber( int drive, DWORD serial )
586 {
587     if (!DRIVE_IsValid( drive )) return 0;
588     if ((DOSDrives[drive].read_volinfo) &&
589         (DOSDrives[drive].type != TYPE_CDROM))
590         FIXME("Setting the serial number is useless for drive %c: until writing it is properly implemented, as this drive reads it from the device.\n", 'A'+drive);
591     DOSDrives[drive].serial_conf = serial;
592     return 1;
593 }
594
595
596 /***********************************************************************
597  *           DRIVE_GetType
598  */
599 DRIVETYPE DRIVE_GetType( int drive )
600 {
601     if (!DRIVE_IsValid( drive )) return TYPE_INVALID;
602     return DOSDrives[drive].type;
603 }
604
605
606 /***********************************************************************
607  *           DRIVE_GetFlags
608  */
609 UINT DRIVE_GetFlags( int drive )
610 {
611     if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
612     return DOSDrives[drive].flags;
613 }
614
615
616 /***********************************************************************
617  *           DRIVE_Chdir
618  */
619 int DRIVE_Chdir( int drive, const char *path )
620 {
621     DOS_FULL_NAME full_name;
622     char buffer[MAX_PATHNAME_LEN];
623     LPSTR unix_cwd;
624     BY_HANDLE_FILE_INFORMATION info;
625     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
626
627     strcpy( buffer, "A:" );
628     buffer[0] += drive;
629     TRACE("(%c:,%s)\n", buffer[0], path );
630     lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
631
632     if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
633     if (!FILE_Stat( full_name.long_name, &info )) return 0;
634     if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
635     {
636         SetLastError( ERROR_FILE_NOT_FOUND );
637         return 0;
638     }
639     unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
640     while (*unix_cwd == '/') unix_cwd++;
641
642     TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
643                    'A' + drive, unix_cwd, full_name.short_name + 3 );
644
645     HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
646     HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
647     DOSDrives[drive].dos_cwd  = HEAP_strdupA( GetProcessHeap(), 0,
648                                               full_name.short_name + 3 );
649     DOSDrives[drive].unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, unix_cwd );
650
651     if (pTask && (pTask->curdrive & 0x80) && 
652         ((pTask->curdrive & ~0x80) == drive))
653     {
654         lstrcpynA( pTask->curdir, full_name.short_name + 2,
655                      sizeof(pTask->curdir) );
656         DRIVE_LastTask = GetCurrentTask();
657     }
658     return 1;
659 }
660
661
662 /***********************************************************************
663  *           DRIVE_Disable
664  */
665 int DRIVE_Disable( int drive  )
666 {
667     if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
668     {
669         SetLastError( ERROR_INVALID_DRIVE );
670         return 0;
671     }
672     DOSDrives[drive].flags |= DRIVE_DISABLED;
673     return 1;
674 }
675
676
677 /***********************************************************************
678  *           DRIVE_Enable
679  */
680 int DRIVE_Enable( int drive  )
681 {
682     if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
683     {
684         SetLastError( ERROR_INVALID_DRIVE );
685         return 0;
686     }
687     DOSDrives[drive].flags &= ~DRIVE_DISABLED;
688     return 1;
689 }
690
691
692 /***********************************************************************
693  *           DRIVE_SetLogicalMapping
694  */
695 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
696 {
697  /* If new_drive is already valid, do nothing and return 0
698     otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
699   
700     DOSDRIVE *old, *new;
701     
702     old = DOSDrives + existing_drive;
703     new = DOSDrives + new_drive;
704
705     if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
706         !old->root ||
707         (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
708     {
709         SetLastError( ERROR_INVALID_DRIVE );
710         return 0;
711     }
712
713     if ( new->root )
714     {
715         TRACE("Can\'t map drive %c to drive %c - drive %c already exists\n",
716               'A' + existing_drive, 'A' + new_drive, 'A' + new_drive );
717         /* it is already mapped there, so return success */
718         if (!strcmp(old->root,new->root))
719             return 1;
720         return 0;
721     }
722
723     new->root = HEAP_strdupA( GetProcessHeap(), 0, old->root );
724     new->dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->dos_cwd );
725     new->unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->unix_cwd );
726     memcpy ( new->label_conf, old->label_conf, 12 );
727     new->serial_conf = old->serial_conf;
728     new->type = old->type;
729     new->flags = old->flags;
730     new->dev = old->dev;
731     new->ino = old->ino;
732
733     TRACE("Drive %c is now equal to drive %c\n",
734           'A' + new_drive, 'A' + existing_drive );
735
736     return 1;
737 }
738
739
740 /***********************************************************************
741  *           DRIVE_OpenDevice
742  *
743  * Open the drive raw device and return a Unix fd (or -1 on error).
744  */
745 int DRIVE_OpenDevice( int drive, int flags )
746 {
747     if (!DRIVE_IsValid( drive )) return -1;
748     return open( DOSDrives[drive].device, flags );
749 }
750
751
752 /***********************************************************************
753  *           DRIVE_RawRead
754  *
755  * Read raw sectors from a device
756  */
757 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
758 {
759     int fd;
760
761     if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
762     {
763         lseek( fd, begin * 512, SEEK_SET );
764         /* FIXME: check errors */
765         read( fd, dataptr, nr_sect * 512 );
766         close( fd );
767     }
768     else
769     {
770         memset(dataptr, 0, nr_sect * 512);
771                 if (fake_success)
772         {
773                         if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
774                         if (begin == 1) *dataptr = 0xf8;
775                 }
776                 else
777                 return 0;
778     }
779         return 1;
780 }
781
782
783 /***********************************************************************
784  *           DRIVE_RawWrite
785  *
786  * Write raw sectors to a device
787  */
788 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
789 {
790         int fd;
791
792     if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
793     {
794         lseek( fd, begin * 512, SEEK_SET );
795         /* FIXME: check errors */
796         write( fd, dataptr, nr_sect * 512 );
797         close( fd );
798     }
799     else
800         if (!(fake_success))
801                 return 0;
802
803         return 1;
804 }
805
806
807 /***********************************************************************
808  *           DRIVE_GetFreeSpace
809  */
810 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size, 
811                                PULARGE_INTEGER available )
812 {
813     struct statfs info;
814     unsigned long long  bigsize,bigavail=0;
815
816     if (!DRIVE_IsValid(drive))
817     {
818         SetLastError( ERROR_INVALID_DRIVE );
819         return 0;
820     }
821
822 /* FIXME: add autoconf check for this */
823 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
824     if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
825 #else
826     if (statfs( DOSDrives[drive].root, &info) < 0)
827 #endif
828     {
829         FILE_SetDosError();
830         WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
831         return 0;
832     }
833
834     bigsize = (unsigned long long)info.f_bsize 
835       * (unsigned long long)info.f_blocks;
836 #ifdef STATFS_HAS_BAVAIL
837     bigavail = (unsigned long long)info.f_bavail 
838       * (unsigned long long)info.f_bsize;
839 #else
840 # ifdef STATFS_HAS_BFREE
841     bigavail = (unsigned long long)info.f_bfree 
842       * (unsigned long long)info.f_bsize;
843 # else
844 #  error "statfs has no bfree/bavail member!"
845 # endif
846 #endif
847     size->s.LowPart = (DWORD)bigsize;
848     size->s.HighPart = (DWORD)(bigsize>>32);
849     available->s.LowPart = (DWORD)bigavail;
850     available->s.HighPart = (DWORD)(bigavail>>32);
851     return 1;
852 }
853
854 /***********************************************************************
855  *       DRIVE_GetCurrentDirectory
856  * Returns "X:\\path\\etc\\".
857  *
858  * Despite the API description, return required length including the 
859  * terminating null when buffer too small. This is the real behaviour.
860 */
861
862 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
863 {
864     UINT ret;
865     const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
866
867     assert(s);
868     ret = strlen(s) + 3; /* length of WHOLE current directory */
869     if (ret >= buflen) return ret + 1;
870     lstrcpynA( buf, "A:\\", MIN( 4, buflen ) );
871     if (buflen) buf[0] += DRIVE_GetCurrentDrive();
872     if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
873     return ret;
874 }
875
876 /***********************************************************************
877  *           GetDiskFreeSpace16   (KERNEL.422)
878  */
879 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
880                                   LPDWORD sector_bytes, LPDWORD free_clusters,
881                                   LPDWORD total_clusters )
882 {
883     return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
884                                 free_clusters, total_clusters );
885 }
886
887
888 /***********************************************************************
889  *           GetDiskFreeSpace32A   (KERNEL32.206)
890  *
891  * Fails if expression resulting from current drive's dir and "root"
892  * is not a root dir of the target drive.
893  *
894  * UNDOC: setting some LPDWORDs to NULL is perfectly possible 
895  * if the corresponding info is unneeded.
896  *
897  * FIXME: needs to support UNC names from Win95 OSR2 on.
898  *
899  * Behaviour under Win95a:
900  * CurrDir     root   result
901  * "E:\\TEST"  "E:"   FALSE
902  * "E:\\"      "E:"   TRUE
903  * "E:\\"      "E"    FALSE
904  * "E:\\"      "\\"   TRUE
905  * "E:\\TEST"  "\\"   TRUE
906  * "E:\\TEST"  ":\\"  FALSE
907  * "E:\\TEST"  "E:\\" TRUE
908  * "E:\\TEST"  ""     FALSE
909  * "E:\\"      ""     FALSE (!)
910  * "E:\\"      0x0    TRUE
911  * "E:\\TEST"  0x0    TRUE  (!)
912  * "E:\\TEST"  "C:"   TRUE  (when CurrDir of "C:" set to "\\")
913  * "E:\\TEST"  "C:"   FALSE (when CurrDir of "C:" set to "\\TEST")
914  */
915 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
916                                    LPDWORD sector_bytes, LPDWORD free_clusters,
917                                    LPDWORD total_clusters )
918 {
919     int drive;
920     ULARGE_INTEGER size,available;
921     LPCSTR path;
922     DWORD cluster_sec;
923
924     if ((!root) || (strcmp(root,"\\") == 0))
925         drive = DRIVE_GetCurrentDrive();
926     else
927     if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
928     {
929         drive = toupper(root[0]) - 'A';
930         path = &root[2];
931         if (path[0] == '\0')
932             path = DRIVE_GetDosCwd(drive);
933         else
934         if (path[0] == '\\')
935             path++;
936         if (strlen(path)) /* oops, we are in a subdir */
937             return FALSE;
938     }
939     else
940         return FALSE;
941
942     if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
943
944     /* Cap the size and available at 2GB as per specs.  */
945     if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
946         {
947           size.s.HighPart = 0;
948           size.s.LowPart = 0x7fffffff;
949         }
950     if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
951       {
952         available.s.HighPart =0;
953         available.s.LowPart = 0x7fffffff;
954       }
955     if (DRIVE_GetType(drive)==TYPE_CDROM) {
956         if (sector_bytes)
957         *sector_bytes    = 2048;
958         size.s.LowPart            /= 2048;
959         available.s.LowPart       /= 2048;
960     } else {
961         if (sector_bytes)
962         *sector_bytes    = 512;
963         size.s.LowPart            /= 512;
964         available.s.LowPart       /= 512;
965     }
966     /* fixme: probably have to adjust those variables too for CDFS */
967     cluster_sec = 1;
968     while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
969
970     if (cluster_sectors)
971         *cluster_sectors = cluster_sec;
972     if (free_clusters)
973         *free_clusters   = available.s.LowPart / cluster_sec;
974     if (total_clusters)
975         *total_clusters  = size.s.LowPart / cluster_sec;
976     return TRUE;
977 }
978
979
980 /***********************************************************************
981  *           GetDiskFreeSpace32W   (KERNEL32.207)
982  */
983 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
984                                    LPDWORD sector_bytes, LPDWORD free_clusters,
985                                    LPDWORD total_clusters )
986 {
987     LPSTR xroot;
988     BOOL ret;
989
990     xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
991     ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
992                                free_clusters, total_clusters );
993     HeapFree( GetProcessHeap(), 0, xroot );
994     return ret;
995 }
996
997
998 /***********************************************************************
999  *           GetDiskFreeSpaceEx32A   (KERNEL32.871)
1000  *
1001  *  This function is used to aquire the size of the available and
1002  *  total space on a logical volume.
1003  *
1004  * RETURNS
1005  *
1006  *  Zero on failure, nonzero upon success. Use GetLastError to obtain
1007  *  detailed error information.
1008  *
1009  */
1010 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1011                                      PULARGE_INTEGER avail,
1012                                      PULARGE_INTEGER total,
1013                                      PULARGE_INTEGER totalfree)
1014 {
1015     int drive;
1016     ULARGE_INTEGER size,available;
1017
1018     if (!root) drive = DRIVE_GetCurrentDrive();
1019     else
1020     {
1021         if ((root[1]) && ((root[1] != ':') || (root[2] != '\\')))
1022         {
1023             FIXME("there are valid root names which are not supported yet\n");
1024             /* ..like UNC names, for instance. */
1025
1026             WARN("invalid root '%s'\n", root );
1027             return FALSE;
1028         }
1029         drive = toupper(root[0]) - 'A';
1030     }
1031
1032     if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1033
1034     if (total)
1035     {
1036         total->s.HighPart = size.s.HighPart;
1037         total->s.LowPart = size.s.LowPart ;
1038     }
1039
1040     if (totalfree)
1041     {
1042         totalfree->s.HighPart = available.s.HighPart;
1043         totalfree->s.LowPart = available.s.LowPart ;
1044     }
1045
1046     if (avail)
1047     {
1048         if (FIXME_ON(dosfs))
1049         {
1050             /* On Windows2000, we need to check the disk quota
1051                allocated for the user owning the calling process. We
1052                don't want to be more obtrusive than necessary with the
1053                FIXME messages, so don't print the FIXME unless Wine is
1054                actually masquerading as Windows2000. */
1055
1056             OSVERSIONINFOA ovi;
1057             ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1058             if (GetVersionExA(&ovi))
1059             {
1060               if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1061                   FIXME("no per-user quota support yet\n");
1062             }
1063         }
1064
1065         /* Quick hack, should eventually be fixed to work 100% with
1066            Windows2000 (see comment above). */
1067         avail->s.HighPart = available.s.HighPart;
1068         avail->s.LowPart = available.s.LowPart ;
1069     }
1070
1071     return TRUE;
1072 }
1073
1074 /***********************************************************************
1075  *           GetDiskFreeSpaceEx32W   (KERNEL32.873)
1076  */
1077 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1078                                      PULARGE_INTEGER total,
1079                                      PULARGE_INTEGER  totalfree)
1080 {
1081     LPSTR xroot;
1082     BOOL ret;
1083
1084     xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1085     ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1086     HeapFree( GetProcessHeap(), 0, xroot );
1087     return ret;
1088 }
1089
1090 /***********************************************************************
1091  *           GetDriveType16   (KERNEL.136)
1092  * This function returns the type of a drive in Win16. 
1093  * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1094  * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1095  * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1096  * do any pseudo-clever changes.
1097  *
1098  * RETURNS
1099  *      drivetype DRIVE_xxx
1100  */
1101 UINT16 WINAPI GetDriveType16(
1102         UINT16 drive    /* [in] number (NOT letter) of drive */
1103 ) {
1104     TRACE("(%c:)\n", 'A' + drive );
1105     switch(DRIVE_GetType(drive))
1106     {
1107     case TYPE_FLOPPY:  return DRIVE_REMOVABLE;
1108     case TYPE_HD:      return DRIVE_FIXED;
1109     case TYPE_CDROM:   return DRIVE_REMOTE;
1110     case TYPE_NETWORK: return DRIVE_REMOTE;
1111     case TYPE_INVALID:
1112     default:           return DRIVE_CANNOTDETERMINE;
1113     }
1114 }
1115
1116
1117 /***********************************************************************
1118  *           GetDriveType32A   (KERNEL32.208)
1119  *
1120  * Returns the type of the disk drive specified.  If root is NULL the
1121  * root of the current directory is used.
1122  *
1123  * RETURNS
1124  *
1125  *  Type of drive (from Win32 SDK):
1126  *
1127  *   DRIVE_UNKNOWN     unable to find out anything about the drive
1128  *   DRIVE_NO_ROOT_DIR nonexistand root dir
1129  *   DRIVE_REMOVABLE   the disk can be removed from the machine
1130  *   DRIVE_FIXED       the disk can not be removed from the machine
1131  *   DRIVE_REMOTE      network disk
1132  *   DRIVE_CDROM       CDROM drive
1133  *   DRIVE_RAMDISK     virtual disk in ram
1134  *
1135  *   DRIVE_DOESNOTEXIST    XXX Not valid return value
1136  *   DRIVE_CANNOTDETERMINE XXX Not valid return value
1137  *   
1138  * BUGS
1139  *
1140  *  Currently returns DRIVE_DOESNOTEXIST and DRIVE_CANNOTDETERMINE
1141  *  when it really should return DRIVE_NO_ROOT_DIR and DRIVE_UNKNOWN.
1142  *  Why were the former defines used?
1143  *
1144  *  DRIVE_RAMDISK is unsupported.
1145  */
1146 UINT WINAPI GetDriveTypeA(LPCSTR root /* String describing drive */)
1147 {
1148     int drive;
1149     TRACE("(%s)\n", debugstr_a(root));
1150
1151     if (NULL == root) drive = DRIVE_GetCurrentDrive();
1152     else
1153     {
1154         if ((root[1]) && (root[1] != ':'))
1155         {
1156             WARN("invalid root '%s'\n", debugstr_a(root));
1157             return DRIVE_DOESNOTEXIST;
1158         }
1159         drive = toupper(root[0]) - 'A';
1160     }
1161     switch(DRIVE_GetType(drive))
1162     {
1163     case TYPE_FLOPPY:  return DRIVE_REMOVABLE;
1164     case TYPE_HD:      return DRIVE_FIXED;
1165     case TYPE_CDROM:   return DRIVE_CDROM;
1166     case TYPE_NETWORK: return DRIVE_REMOTE;
1167     case TYPE_INVALID: return DRIVE_DOESNOTEXIST;
1168     default:           return DRIVE_CANNOTDETERMINE;
1169     }
1170 }
1171
1172
1173 /***********************************************************************
1174  *           GetDriveType32W   (KERNEL32.209)
1175  */
1176 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1177 {
1178     LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1179     UINT ret = GetDriveTypeA( xpath );
1180     HeapFree( GetProcessHeap(), 0, xpath );
1181     return ret;
1182 }
1183
1184
1185 /***********************************************************************
1186  *           GetCurrentDirectory16   (KERNEL.411)
1187  */
1188 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1189 {
1190     return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1191 }
1192
1193
1194 /***********************************************************************
1195  *           GetCurrentDirectory32A   (KERNEL32.196)
1196  */
1197 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1198 {
1199     UINT ret;
1200     char longname[MAX_PATHNAME_LEN];
1201     char shortname[MAX_PATHNAME_LEN];
1202     ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1203     if ( ret > MAX_PATHNAME_LEN ) {
1204       ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1205       return ret;
1206     }
1207     GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1208     ret = lstrlenA( longname ) + 1;
1209     if (ret > buflen) return ret;
1210     lstrcpyA(buf, longname);
1211     return ret - 1;
1212 }
1213
1214 /***********************************************************************
1215  *           GetCurrentDirectory32W   (KERNEL32.197)
1216  */
1217 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1218 {
1219     LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1220     UINT ret = GetCurrentDirectoryA( buflen, xpath );
1221     if (ret < buflen) lstrcpyAtoW ( buf, xpath );
1222     HeapFree( GetProcessHeap(), 0, xpath );
1223     return ret;
1224 }
1225
1226
1227 /***********************************************************************
1228  *           SetCurrentDirectory   (KERNEL.412)
1229  */
1230 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1231 {
1232     return SetCurrentDirectoryA( dir );
1233 }
1234
1235
1236 /***********************************************************************
1237  *           SetCurrentDirectory32A   (KERNEL32.479)
1238  */
1239 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1240 {
1241     int olddrive, drive = DRIVE_GetCurrentDrive();
1242
1243     if (!dir) {
1244         ERR_(file)("(NULL)!\n");
1245         return FALSE;
1246     }
1247     if (dir[0] && (dir[1]==':'))
1248     {
1249         drive = tolower( *dir ) - 'a';
1250         dir += 2;
1251     }
1252
1253     /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1254        sets pTask->curdir only if pTask->curdrive is drive */
1255     olddrive = drive; /* in case DRIVE_Chdir fails */
1256     if (!(DRIVE_SetCurrentDrive( drive )))
1257         return FALSE;
1258     /* FIXME: what about empty strings? Add a \\ ? */
1259     if (!DRIVE_Chdir( drive, dir )) {
1260         DRIVE_SetCurrentDrive(olddrive);
1261         return FALSE;
1262     }
1263     return TRUE;
1264 }
1265
1266
1267 /***********************************************************************
1268  *           SetCurrentDirectory32W   (KERNEL32.480)
1269  */
1270 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1271 {
1272     LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1273     BOOL res = SetCurrentDirectoryA( dir );
1274     HeapFree( GetProcessHeap(), 0, dir );
1275     return res;
1276 }
1277
1278
1279 /***********************************************************************
1280  *           GetLogicalDriveStrings32A   (KERNEL32.231)
1281  */
1282 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1283 {
1284     int drive, count;
1285
1286     for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1287         if (DRIVE_IsValid(drive)) count++;
1288     if ((count * 4) + 1 <= len)
1289     {
1290         LPSTR p = buffer;
1291         for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1292             if (DRIVE_IsValid(drive))
1293             {
1294                 *p++ = 'a' + drive;
1295                 *p++ = ':';
1296                 *p++ = '\\';
1297                 *p++ = '\0';
1298             }
1299         *p = '\0';
1300        return count * 4;
1301     }
1302     else
1303       return (count * 4) + 1;/* account for terminating null */
1304     /* The API tells about these different return values */
1305 }
1306
1307
1308 /***********************************************************************
1309  *           GetLogicalDriveStrings32W   (KERNEL32.232)
1310  */
1311 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1312 {
1313     int drive, count;
1314
1315     for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1316         if (DRIVE_IsValid(drive)) count++;
1317     if (count * 4 * sizeof(WCHAR) <= len)
1318     {
1319         LPWSTR p = buffer;
1320         for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1321             if (DRIVE_IsValid(drive))
1322             {
1323                 *p++ = (WCHAR)('a' + drive);
1324                 *p++ = (WCHAR)':';
1325                 *p++ = (WCHAR)'\\';
1326                 *p++ = (WCHAR)'\0';
1327             }
1328         *p = (WCHAR)'\0';
1329     }
1330     return count * 4 * sizeof(WCHAR);
1331 }
1332
1333
1334 /***********************************************************************
1335  *           GetLogicalDrives   (KERNEL32.233)
1336  */
1337 DWORD WINAPI GetLogicalDrives(void)
1338 {
1339     DWORD ret = 0;
1340     int drive;
1341
1342     for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1343     {
1344         if ( (DRIVE_IsValid(drive)) ||
1345             (DOSDrives[drive].type == TYPE_CDROM)) /* audio CD is also valid */
1346             ret |= (1 << drive);
1347     }
1348     return ret;
1349 }
1350
1351
1352 /***********************************************************************
1353  *           GetVolumeInformation32A   (KERNEL32.309)
1354  */
1355 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1356                                        DWORD label_len, DWORD *serial,
1357                                        DWORD *filename_len, DWORD *flags,
1358                                        LPSTR fsname, DWORD fsname_len )
1359 {
1360     int drive;
1361     char *cp;
1362
1363     /* FIXME, SetLastError()s missing */
1364
1365     if (!root) drive = DRIVE_GetCurrentDrive();
1366     else
1367     {
1368         if ((root[1]) && (root[1] != ':'))
1369         {
1370             WARN("invalid root '%s'\n",root);
1371             return FALSE;
1372         }
1373         drive = toupper(root[0]) - 'A';
1374     }
1375     if (!DRIVE_IsValid( drive )) return FALSE;
1376     if (label)
1377     {
1378        lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1379        for (cp = label; *cp; cp++);
1380        while (cp != label && *(cp-1) == ' ') cp--;
1381        *cp = '\0';
1382     }
1383     if (serial) *serial = DRIVE_GetSerialNumber(drive);
1384
1385     /* Set the filesystem information */
1386     /* Note: we only emulate a FAT fs at present */
1387
1388     if (filename_len) {
1389         if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1390             *filename_len = 12;
1391         else
1392             *filename_len = 255;
1393     }
1394     if (flags)
1395       {
1396        *flags=0;
1397        if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1398          *flags|=FS_CASE_SENSITIVE;
1399        if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1400          *flags|=FS_CASE_IS_PRESERVED;
1401       }
1402     if (fsname) {
1403         /* Diablo checks that return code ... */
1404         if (DRIVE_GetType(drive)==TYPE_CDROM)
1405             lstrcpynA( fsname, "CDFS", fsname_len );
1406         else
1407             lstrcpynA( fsname, "FAT", fsname_len );
1408     }
1409     return TRUE;
1410 }
1411
1412
1413 /***********************************************************************
1414  *           GetVolumeInformation32W   (KERNEL32.310)
1415  */
1416 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1417                                        DWORD label_len, DWORD *serial,
1418                                        DWORD *filename_len, DWORD *flags,
1419                                        LPWSTR fsname, DWORD fsname_len )
1420 {
1421     LPSTR xroot    = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1422     LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1423     LPSTR xfsname  = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1424     BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1425                                           filename_len, flags, xfsname,
1426                                           fsname_len );
1427     if (ret)
1428     {
1429         if (label) lstrcpyAtoW( label, xvolname );
1430         if (fsname) lstrcpyAtoW( fsname, xfsname );
1431     }
1432     HeapFree( GetProcessHeap(), 0, xroot );
1433     HeapFree( GetProcessHeap(), 0, xvolname );
1434     HeapFree( GetProcessHeap(), 0, xfsname );
1435     return ret;
1436 }
1437
1438 /***********************************************************************
1439  *           SetVolumeLabelA   (KERNEL32.675)
1440  */
1441 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1442 {
1443     int drive;
1444     
1445     /* FIXME, SetLastErrors missing */
1446
1447     if (!root) drive = DRIVE_GetCurrentDrive();
1448     else
1449     {
1450         if ((root[1]) && (root[1] != ':'))
1451         {
1452             WARN("invalid root '%s'\n",root);
1453             return FALSE;
1454         }
1455         drive = toupper(root[0]) - 'A';
1456     }
1457     if (!DRIVE_IsValid( drive )) return FALSE;
1458
1459     /* some copy protection stuff check this */
1460     if (DRIVE_GetType( drive ) == TYPE_CDROM) return FALSE;
1461
1462     FIXME("(%s,%s),stub!\n", root, volname);
1463     return TRUE;
1464 }
1465
1466 /***********************************************************************
1467  *           SetVolumeLabelW   (KERNEL32.676)
1468  */
1469 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1470 {
1471     LPSTR xroot, xvol;
1472     BOOL ret;
1473
1474     xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1475     xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1476     ret = SetVolumeLabelA( xroot, xvol );
1477     HeapFree( GetProcessHeap(), 0, xroot );
1478     HeapFree( GetProcessHeap(), 0, xvol );
1479     return ret;
1480 }