Changed the GDI driver interface to pass an opaque PHYSDEV pointer
[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  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25
26 #include "config.h"
27 #include "wine/port.h"
28
29 #include <assert.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <unistd.h>
39
40 #ifdef HAVE_SYS_PARAM_H
41 # include <sys/param.h>
42 #endif
43 #ifdef STATFS_DEFINED_BY_SYS_VFS
44 # include <sys/vfs.h>
45 #else
46 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
47 #  include <sys/mount.h>
48 # else
49 #  ifdef STATFS_DEFINED_BY_SYS_STATFS
50 #   include <sys/statfs.h>
51 #  endif
52 # endif
53 #endif
54
55 #include "winbase.h"
56 #include "ntddk.h"
57 #include "wine/winbase16.h"   /* for GetCurrentTask */
58 #include "winerror.h"
59 #include "drive.h"
60 #include "file.h"
61 #include "heap.h"
62 #include "msdos.h"
63 #include "options.h"
64 #include "task.h"
65 #include "wine/debug.h"
66 #include "wine/server.h"
67 #include "winioctl.h"
68 #include "ntddstor.h"
69 #include "ntddcdrm.h"
70
71 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
72 WINE_DECLARE_DEBUG_CHANNEL(file);
73
74 typedef struct
75 {
76     char     *root;      /* root dir in Unix format without trailing / */
77     char     *dos_cwd;   /* cwd in DOS format without leading or trailing \ */
78     char     *unix_cwd;  /* cwd in Unix format without leading or trailing / */
79     char     *device;    /* raw device path */
80     char      label_conf[12]; /* drive label as cfg'd in wine config */
81     char      label_read[12]; /* drive label as read from device */
82     DWORD     serial_conf;    /* drive serial number as cfg'd in wine config */
83     UINT      type;      /* drive type */
84     UINT      flags;     /* drive flags */
85     dev_t     dev;       /* unix device number */
86     ino_t     ino;       /* unix inode number */
87 } DOSDRIVE;
88
89
90 static const char * const DRIVE_Types[] =
91 {
92     "",         /* DRIVE_UNKNOWN */
93     "",         /* DRIVE_NO_ROOT_DIR */
94     "floppy",   /* DRIVE_REMOVABLE */
95     "hd",       /* DRIVE_FIXED */
96     "network",  /* DRIVE_REMOTE */
97     "cdrom",    /* DRIVE_CDROM */
98     "ramdisk"   /* DRIVE_RAMDISK */
99 };
100
101
102 /* Known filesystem types */
103
104 typedef struct
105 {
106     const char *name;
107     UINT      flags;
108 } FS_DESCR;
109
110 static const FS_DESCR DRIVE_Filesystems[] =
111 {
112     { "unix",   DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
113     { "msdos",  DRIVE_SHORT_NAMES },
114     { "dos",    DRIVE_SHORT_NAMES },
115     { "fat",    DRIVE_SHORT_NAMES },
116     { "vfat",   DRIVE_CASE_PRESERVING },
117     { "win95",  DRIVE_CASE_PRESERVING },
118     { NULL, 0 }
119 };
120
121
122 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
123 static int DRIVE_CurDrive = -1;
124
125 static HTASK16 DRIVE_LastTask = 0;
126
127 /* strdup on the process heap */
128 inline static char *heap_strdup( const char *str )
129 {
130     INT len = strlen(str) + 1;
131     LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
132     if (p) memcpy( p, str, len );
133     return p;
134 }
135
136 /***********************************************************************
137  *           DRIVE_GetDriveType
138  */
139 static UINT DRIVE_GetDriveType( const char *name )
140 {
141     char buffer[20];
142     int i;
143
144     PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
145     for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
146     {
147         if (!strcasecmp( buffer, DRIVE_Types[i] )) return i;
148     }
149     MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
150         name, buffer );
151     return DRIVE_FIXED;
152 }
153
154
155 /***********************************************************************
156  *           DRIVE_GetFSFlags
157  */
158 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
159 {
160     const FS_DESCR *descr;
161
162     for (descr = DRIVE_Filesystems; descr->name; descr++)
163         if (!strcasecmp( value, descr->name )) return descr->flags;
164     MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
165         name, value );
166     return DRIVE_CASE_PRESERVING;
167 }
168
169
170 /***********************************************************************
171  *           DRIVE_Init
172  */
173 int DRIVE_Init(void)
174 {
175     int i, len, count = 0;
176     char name[] = "Drive A";
177     char drive_env[] = "=A:";
178     char path[MAX_PATHNAME_LEN];
179     char buffer[80];
180     struct stat drive_stat_buffer;
181     char *p;
182     DOSDRIVE *drive;
183
184     for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
185     {
186         PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
187         if (path[0])
188         {
189             p = path + strlen(path) - 1;
190             while ((p > path) && (*p == '/')) *p-- = '\0';
191
192             if (path[0] == '/')
193             {
194                 drive->root = heap_strdup( path );
195             }
196             else
197             {
198                 /* relative paths are relative to config dir */
199                 const char *config = get_config_dir();
200                 drive->root = HeapAlloc( GetProcessHeap(), 0, strlen(config) + strlen(path) + 2 );
201                 sprintf( drive->root, "%s/%s", config, path );
202             }
203
204             if (stat( drive->root, &drive_stat_buffer ))
205             {
206                 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
207                         drive->root, strerror(errno), 'A' + i);
208                 HeapFree( GetProcessHeap(), 0, drive->root );
209                 drive->root = NULL;
210                 continue;
211             }
212             if (!S_ISDIR(drive_stat_buffer.st_mode))
213             {
214                 MESSAGE("%s is not a directory, ignoring drive %c:\n",
215                         drive->root, 'A' + i );
216                 HeapFree( GetProcessHeap(), 0, drive->root );
217                 drive->root = NULL;
218                 continue;
219             }
220
221             drive->dos_cwd  = heap_strdup( "" );
222             drive->unix_cwd = heap_strdup( "" );
223             drive->type     = DRIVE_GetDriveType( name );
224             drive->device   = NULL;
225             drive->flags    = 0;
226             drive->dev      = drive_stat_buffer.st_dev;
227             drive->ino      = drive_stat_buffer.st_ino;
228
229             /* Get the drive label */
230             PROFILE_GetWineIniString( name, "Label", "", drive->label_conf, 12 );
231             if ((len = strlen(drive->label_conf)) < 11)
232             {
233                 /* Pad label with spaces */
234                 memset( drive->label_conf + len, ' ', 11 - len );
235                 drive->label_conf[11] = '\0';
236             }
237
238             /* Get the serial number */
239             PROFILE_GetWineIniString( name, "Serial", "12345678",
240                                       buffer, sizeof(buffer) );
241             drive->serial_conf = strtoul( buffer, NULL, 16 );
242
243             /* Get the filesystem type */
244             PROFILE_GetWineIniString( name, "Filesystem", "win95",
245                                       buffer, sizeof(buffer) );
246             drive->flags = DRIVE_GetFSFlags( name, buffer );
247
248             /* Get the device */
249             PROFILE_GetWineIniString( name, "Device", "",
250                                       buffer, sizeof(buffer) );
251             if (buffer[0])
252             {
253                 drive->device = heap_strdup( buffer );
254                 if (PROFILE_GetWineIniBool( name, "ReadVolInfo", 1))
255                     drive->flags |= DRIVE_READ_VOL_INFO;
256             }
257
258             /* Get the FailReadOnly flag */
259             if (PROFILE_GetWineIniBool( name, "FailReadOnly", 0 ))
260                 drive->flags |= DRIVE_FAIL_READ_ONLY;
261
262             /* Make the first hard disk the current drive */
263             if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
264                 DRIVE_CurDrive = i;
265
266             count++;
267             TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
268                   "flags=%08x dev=%x ino=%x\n",
269                   name, drive->root, DRIVE_Types[drive->type],
270                   drive->label_conf, drive->serial_conf, drive->flags,
271                   (int)drive->dev, (int)drive->ino );
272         }
273         else WARN("%s: not defined\n", name );
274     }
275
276     if (!count) 
277     {
278         MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
279         /* Create a C drive pointing to Unix root dir */
280         DOSDrives[2].root     = heap_strdup( "/" );
281         DOSDrives[2].dos_cwd  = heap_strdup( "" );
282         DOSDrives[2].unix_cwd = heap_strdup( "" );
283         strcpy( DOSDrives[2].label_conf, "Drive C    " );
284         DOSDrives[2].serial_conf   = 12345678;
285         DOSDrives[2].type     = DRIVE_FIXED;
286         DOSDrives[2].device   = NULL;
287         DOSDrives[2].flags    = 0;
288         DRIVE_CurDrive = 2;
289     }
290
291     /* Make sure the current drive is valid */
292     if (DRIVE_CurDrive == -1)
293     {
294         for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
295         {
296             if (drive->root && !(drive->flags & DRIVE_DISABLED))
297             {
298                 DRIVE_CurDrive = i;
299                 break;
300             }
301         }
302     }
303
304     /* get current working directory info for all drives */
305     for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
306     {
307         if (!GetEnvironmentVariableA(drive_env, path, sizeof(path))) continue;
308         /* sanity check */
309         if (toupper(path[0]) != drive_env[1] || path[1] != ':') continue;
310         DRIVE_Chdir( i, path + 2 );
311     }
312     return 1;
313 }
314
315
316 /***********************************************************************
317  *           DRIVE_IsValid
318  */
319 int DRIVE_IsValid( int drive )
320 {
321     if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
322     return (DOSDrives[drive].root &&
323             !(DOSDrives[drive].flags & DRIVE_DISABLED));
324 }
325
326
327 /***********************************************************************
328  *           DRIVE_GetCurrentDrive
329  */
330 int DRIVE_GetCurrentDrive(void)
331 {
332     TDB *pTask = TASK_GetCurrent();
333     if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
334     return DRIVE_CurDrive;
335 }
336
337
338 /***********************************************************************
339  *           DRIVE_SetCurrentDrive
340  */
341 int DRIVE_SetCurrentDrive( int drive )
342 {
343     TDB *pTask = TASK_GetCurrent();
344     if (!DRIVE_IsValid( drive ))
345     {
346         SetLastError( ERROR_INVALID_DRIVE );
347         return 0;
348     }
349     TRACE("%c:\n", 'A' + drive );
350     DRIVE_CurDrive = drive;
351     if (pTask) pTask->curdrive = drive | 0x80;
352     chdir(DRIVE_GetUnixCwd(drive));
353     return 1;
354 }
355
356
357 /***********************************************************************
358  *           DRIVE_FindDriveRoot
359  *
360  * Find a drive for which the root matches the beginning of the given path.
361  * This can be used to translate a Unix path into a drive + DOS path.
362  * Return value is the drive, or -1 on error. On success, path is modified
363  * to point to the beginning of the DOS path.
364  */
365 int DRIVE_FindDriveRoot( const char **path )
366 {
367     /* idea: check at all '/' positions.
368      * If the device and inode of that path is identical with the
369      * device and inode of the current drive then we found a solution.
370      * If there is another drive pointing to a deeper position in
371      * the file tree, we want to find that one, not the earlier solution.
372      */
373     int drive, rootdrive = -1;
374     char buffer[MAX_PATHNAME_LEN];
375     char *next = buffer;
376     const char *p = *path;
377     struct stat st;
378
379     strcpy( buffer, "/" );
380     for (;;)
381     {
382         if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
383
384         /* Find the drive */
385
386         for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
387         {
388            if (!DOSDrives[drive].root ||
389                (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
390
391            if ((DOSDrives[drive].dev == st.st_dev) &&
392                (DOSDrives[drive].ino == st.st_ino))
393            {
394                rootdrive = drive;
395                *path = p;
396                break;
397            }
398         }
399
400         /* Get the next path component */
401
402         *next++ = '/';
403         while ((*p == '/') || (*p == '\\')) p++;
404         if (!*p) break;
405         while (!IS_END_OF_NAME(*p)) *next++ = *p++;
406         *next = 0;
407     }
408     *next = 0;
409
410     if (rootdrive != -1)
411         TRACE("%s -> drive %c:, root='%s', name='%s'\n",
412               buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, *path );
413     return rootdrive;
414 }
415
416
417 /***********************************************************************
418  *           DRIVE_GetRoot
419  */
420 const char * DRIVE_GetRoot( int drive )
421 {
422     if (!DRIVE_IsValid( drive )) return NULL;
423     return DOSDrives[drive].root;
424 }
425
426
427 /***********************************************************************
428  *           DRIVE_GetDosCwd
429  */
430 const char * DRIVE_GetDosCwd( int drive )
431 {
432     TDB *pTask = TASK_GetCurrent();
433     if (!DRIVE_IsValid( drive )) return NULL;
434
435     /* Check if we need to change the directory to the new task. */
436     if (pTask && (pTask->curdrive & 0x80) &&    /* The task drive is valid */
437         ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
438         (DRIVE_LastTask != GetCurrentTask()))   /* and the task changed */
439     {
440         /* Perform the task-switch */
441         if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
442         DRIVE_LastTask = GetCurrentTask();
443     }
444     return DOSDrives[drive].dos_cwd;
445 }
446
447
448 /***********************************************************************
449  *           DRIVE_GetUnixCwd
450  */
451 const char * DRIVE_GetUnixCwd( int drive )
452 {
453     TDB *pTask = TASK_GetCurrent();
454     if (!DRIVE_IsValid( drive )) return NULL;
455
456     /* Check if we need to change the directory to the new task. */
457     if (pTask && (pTask->curdrive & 0x80) &&    /* The task drive is valid */
458         ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
459         (DRIVE_LastTask != GetCurrentTask()))   /* and the task changed */
460     {
461         /* Perform the task-switch */
462         if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
463         DRIVE_LastTask = GetCurrentTask();
464     }
465     return DOSDrives[drive].unix_cwd;
466 }
467
468
469 /***********************************************************************
470  *           DRIVE_GetDevice
471  */
472 const char * DRIVE_GetDevice( int drive )
473 {
474     return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
475 }
476
477 /******************************************************************
478  *              static WORD CDROM_Data_FindBestVoldesc
479  *
480  *
481  */
482 static WORD CDROM_Data_FindBestVoldesc(int fd)
483 {
484     BYTE cur_vd_type, max_vd_type = 0;
485     unsigned int offs, best_offs = 0, extra_offs = 0;
486     char sig[3];
487
488     for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
489     {
490         /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
491          * the volume label is displaced forward by 8
492          */
493         lseek(fd, offs + 11, SEEK_SET); /* check for non-ISO9660 signature */
494         read(fd, &sig, 3);
495         if ((sig[0] == 'R') && (sig[1] == 'O') && (sig[2]=='M'))
496         {
497             extra_offs = 8;
498         }
499         lseek(fd, offs + extra_offs, SEEK_SET);
500         read(fd, &cur_vd_type, 1);
501         if (cur_vd_type == 0xff) /* voldesc set terminator */
502             break;
503         if (cur_vd_type > max_vd_type)
504         {
505             max_vd_type = cur_vd_type;
506             best_offs = offs + extra_offs;
507         }
508     }
509     return best_offs;
510 }
511
512 /***********************************************************************
513  *           DRIVE_ReadSuperblock
514  *
515  * NOTE 
516  *      DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
517  * to check, that they are writing on a FAT filesystem !
518  */
519 int DRIVE_ReadSuperblock (int drive, char * buff)
520 {
521 #define DRIVE_SUPER 96
522     int fd;
523     off_t offs;
524
525     if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
526     if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
527     {
528         struct stat st;
529         if (!DOSDrives[drive].device)
530             ERR("No device configured for drive %c: !\n", 'A'+drive);
531         else
532             ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
533                  (stat(DOSDrives[drive].device, &st)) ?
534                         "not available or symlink not valid ?" : "no permission");
535         ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
536         PROFILE_UsageWineIni();
537         return -1;
538     }
539
540     switch(DOSDrives[drive].type)
541     {
542         case DRIVE_REMOVABLE:
543         case DRIVE_FIXED:
544             offs = 0;
545             break;
546         case DRIVE_CDROM:
547             offs = CDROM_Data_FindBestVoldesc(fd);
548             break;
549         default:
550             offs = 0;
551             break;
552     }
553
554     if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs)) return -4;
555     if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER) return -2;
556
557     switch(DOSDrives[drive].type)
558     {
559         case DRIVE_REMOVABLE:
560         case DRIVE_FIXED:
561             if ((buff[0x26]!=0x29) ||  /* Check for FAT present */
562                 /* FIXME: do really all FAT have their name beginning with
563                    "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
564                 memcmp( buff+0x36,"FAT",3))
565             {
566                 ERR("The filesystem is not FAT !! (device=%s)\n",
567                     DOSDrives[drive].device);
568                 return -3;
569             }
570             break;
571         case DRIVE_CDROM:
572             if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
573                 return -3;
574             /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
575                 break;
576         default:
577                 return -3;
578                 break;
579     }
580
581     return close(fd);
582 }
583
584
585 /***********************************************************************
586  *           DRIVE_WriteSuperblockEntry
587  *
588  * NOTE
589  *      We are writing as little as possible (ie. not the whole SuperBlock)
590  * not to interfere with kernel. The drive can be mounted !
591  */
592 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
593 {
594     int fd;
595
596     if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1) 
597     {
598         ERR("Cannot open the device %s (for writing)\n",
599             DOSDrives[drive].device); 
600         return -1;
601     }
602     if (lseek(fd,ofs,SEEK_SET)!=ofs)
603     {
604         ERR("lseek failed on device %s !\n",
605             DOSDrives[drive].device); 
606         close(fd);
607         return -2;
608     }
609     if (write(fd,buff,len)!=len) 
610     {
611         close(fd);
612         ERR("Cannot write on %s !\n",
613             DOSDrives[drive].device); 
614         return -3;
615     }
616     return close (fd);
617 }
618
619 /******************************************************************
620  *              static HANDLE   CDROM_Open
621  *
622  *
623  */
624 static HANDLE   CDROM_Open(int drive)
625 {
626     char       root[6];
627
628     strcpy(root, "\\\\.\\A:");
629     root[4] += drive;
630
631     return CreateFileA(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
632 }
633
634 /**************************************************************************
635  *                              CDROM_Data_GetLabel             [internal]
636  */
637 DWORD CDROM_Data_GetLabel(int drive, char *label)
638 {
639 #define LABEL_LEN       32+1
640     int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
641     WORD offs = CDROM_Data_FindBestVoldesc(dev);
642     WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
643     DWORD unicode_id = 0;
644  
645     if (offs)
646     {
647         if ((lseek(dev, offs+0x58, SEEK_SET) == offs+0x58)
648         &&  (read(dev, &unicode_id, 3) == 3))
649         {
650             int ver = (unicode_id & 0xff0000) >> 16;
651  
652             if ((lseek(dev, offs+0x28, SEEK_SET) != offs+0x28)
653             ||  (read(dev, &label_read, LABEL_LEN) != LABEL_LEN))
654                 goto failure;
655  
656             close(dev);
657             if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
658             &&  ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
659             { /* yippee, unicode */
660                 int i;
661                 WORD ch;
662                 for (i=0; i<LABEL_LEN;i++)
663                 { /* Motorola -> Intel Unicode conversion :-\ */
664                      ch = label_read[i];
665                      label_read[i] = (ch << 8) | (ch >> 8);
666                 }
667                 WideCharToMultiByte( CP_ACP, 0, label_read, -1, label, 12, NULL, NULL );
668                 label[11] = 0;
669             }
670             else
671             {
672                 strncpy(label, (LPSTR)label_read, 11);
673                 label[11] = '\0';
674             }
675             return 1;
676         }
677     }
678 failure:
679     close(dev);
680     ERR("error reading label !\n");
681     return 0;
682 }
683
684 /**************************************************************************
685  *                              CDROM_GetLabel                  [internal]
686  */
687 static DWORD CDROM_GetLabel(int drive, char *label)
688 {
689     HANDLE              h = CDROM_Open(drive);
690     CDROM_DISK_DATA     cdd;
691     DWORD               br;
692     DWORD               ret = 1;
693
694     if (!h || !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
695         return 0;
696
697     switch (cdd.DiskData & 0x03)
698     {
699     case CDROM_DISK_DATA_TRACK:
700         if (!CDROM_Data_GetLabel(drive, label))
701             ret = 0;
702         break;
703     case CDROM_DISK_AUDIO_TRACK:
704         strcpy(label, "Audio CD   ");
705         break;
706     case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
707         FIXME("Need to get the label of a mixed mode CD: not implemented yet !\n");
708         /* fall through */
709     case 0:
710         ret = 0;
711         break;
712     }
713     TRACE("CD: label is '%s'.\n", label);
714
715     return ret;
716 }
717 /***********************************************************************
718  *           DRIVE_GetLabel
719  */
720 const char * DRIVE_GetLabel( int drive )
721 {
722     int read = 0;
723     char buff[DRIVE_SUPER];
724     int offs = -1;
725
726     if (!DRIVE_IsValid( drive )) return NULL;
727     if (DOSDrives[drive].type == DRIVE_CDROM)
728     {
729         read = CDROM_GetLabel(drive, DOSDrives[drive].label_read); 
730     }
731     else
732     if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
733     {
734         if (DRIVE_ReadSuperblock(drive,(char *) buff))
735             ERR("Invalid or unreadable superblock on %s (%c:).\n",
736                 DOSDrives[drive].device, (char)(drive+'A'));
737         else {
738             if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
739                 DOSDrives[drive].type == DRIVE_FIXED)
740                 offs = 0x2b;
741
742             /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
743             if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
744             DOSDrives[drive].label_read[11]='\0';
745             read = 1;
746         }
747     }
748
749     return (read) ?
750         DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
751 }
752
753 #define CDFRAMES_PERSEC                 75
754 #define CDFRAMES_PERMIN                 (CDFRAMES_PERSEC * 60)
755 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
756 #define FRAME_OF_TOC(toc, idx)  FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
757
758 /**************************************************************************
759  *                              CDROM_Audio_GetSerial           [internal]
760  */
761 static DWORD CDROM_Audio_GetSerial(HANDLE h)
762 {
763     unsigned long serial = 0;
764     int i;
765     WORD wMagic;
766     DWORD dwStart, dwEnd, br;
767     CDROM_TOC toc;
768
769     if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
770         return 0;
771
772     /*
773      * wMagic collects the wFrames from track 1
774      * dwStart, dwEnd collect the beginning and end of the disc respectively, in
775      * frames.
776      * There it is collected for correcting the serial when there are less than
777      * 3 tracks.
778      */
779     wMagic = toc.TrackData[0].Address[2];
780     dwStart = FRAME_OF_TOC(toc, toc.FirstTrack);
781
782     for (i = 0; i <= toc.LastTrack - toc.FirstTrack; i++) {
783         serial += (toc.TrackData[i].Address[0] << 16) | 
784             (toc.TrackData[i].Address[1] << 8) | toc.TrackData[i].Address[2];
785     }
786     dwEnd = FRAME_OF_TOC(toc, toc.LastTrack + 1);
787  
788     if (toc.LastTrack - toc.FirstTrack + 1 < 3)
789         serial += wMagic + (dwEnd - dwStart);
790
791     return serial;
792 }
793
794 /**************************************************************************
795  *                              CDROM_Data_GetSerial            [internal]
796  */
797 static DWORD CDROM_Data_GetSerial(int drive)
798 {
799     int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
800     WORD offs;
801     union {
802         unsigned long val;
803         unsigned char p[4];
804     } serial;
805     BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
806  
807
808     if (dev == -1) return 0;
809     offs = CDROM_Data_FindBestVoldesc(dev);
810
811     serial.val = 0;
812     if (offs)
813     {
814         BYTE buf[2048];
815         OSVERSIONINFOA ovi;
816         int i;
817  
818         lseek(dev, offs, SEEK_SET);
819         read(dev, buf, 2048);
820         /*
821          * OK, another braindead one... argh. Just believe it.
822          * Me$$ysoft chose to reverse the serial number in NT4/W2K.
823          * It's true and nobody will ever be able to change it.
824          */
825         ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
826         GetVersionExA(&ovi);
827         if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&  (ovi.dwMajorVersion >= 4))
828         {
829             b0 = 3; b1 = 2; b2 = 1; b3 = 0;
830         }
831         for (i = 0; i < 2048; i += 4)
832         {
833             /* DON'T optimize this into DWORD !! (breaks overflow) */
834             serial.p[b0] += buf[i+b0];
835             serial.p[b1] += buf[i+b1];
836             serial.p[b2] += buf[i+b2];
837             serial.p[b3] += buf[i+b3];
838         }
839     }
840     close(dev);
841     return serial.val;
842 }
843         
844 /**************************************************************************
845  *                              CDROM_GetSerial                 [internal]
846  */
847 static DWORD CDROM_GetSerial(int drive)
848 {
849     DWORD               serial = 0;
850     HANDLE              h = CDROM_Open(drive);
851     CDROM_DISK_DATA     cdd;
852     DWORD               br;
853
854     if (!h || ! !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
855         return 0;
856
857     switch (cdd.DiskData & 0x03)
858     {
859     case CDROM_DISK_DATA_TRACK:
860         /* hopefully a data CD */
861         serial = CDROM_Data_GetSerial(drive);
862         break;
863     case CDROM_DISK_AUDIO_TRACK:
864         /* fall thru */
865     case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
866         serial = CDROM_Audio_GetSerial(h);
867         break;
868     case 0:
869         break;
870     }
871     
872     if (serial)
873         TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
874
875     CloseHandle(h);
876
877     return serial;
878 }
879
880 /***********************************************************************
881  *           DRIVE_GetSerialNumber
882  */
883 DWORD DRIVE_GetSerialNumber( int drive )
884 {
885     DWORD serial = 0;
886     char buff[DRIVE_SUPER];
887
888     if (!DRIVE_IsValid( drive )) return 0;
889     
890     if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
891     {
892         switch(DOSDrives[drive].type)
893         {
894         case DRIVE_REMOVABLE:
895         case DRIVE_FIXED:
896             if (DRIVE_ReadSuperblock(drive,(char *) buff))
897                 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
898                         " Maybe not FAT?\n" ,
899                         DOSDrives[drive].device, 'A'+drive);
900             else
901                 serial = *((DWORD*)(buff+0x27));
902             break;
903         case DRIVE_CDROM:
904             serial = CDROM_GetSerial(drive);
905             break;
906         default:
907             FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
908         }
909     }
910                 
911     return (serial) ? serial : DOSDrives[drive].serial_conf;
912 }
913
914
915 /***********************************************************************
916  *           DRIVE_SetSerialNumber
917  */
918 int DRIVE_SetSerialNumber( int drive, DWORD serial )
919 {
920     char buff[DRIVE_SUPER];
921
922     if (!DRIVE_IsValid( drive )) return 0;
923
924     if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
925     {
926         if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
927             (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
928         /* check, if the drive has a FAT filesystem */ 
929         if (DRIVE_ReadSuperblock(drive, buff)) return 0;
930         if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
931         return 1;
932     }
933
934     if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
935     DOSDrives[drive].serial_conf = serial;
936     return 1;
937 }
938
939
940 /***********************************************************************
941  *           DRIVE_GetType
942  */
943 static UINT DRIVE_GetType( int drive )
944 {
945     if (!DRIVE_IsValid( drive )) return DRIVE_UNKNOWN;
946     return DOSDrives[drive].type;
947 }
948
949
950 /***********************************************************************
951  *           DRIVE_GetFlags
952  */
953 UINT DRIVE_GetFlags( int drive )
954 {
955     if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
956     return DOSDrives[drive].flags;
957 }
958
959
960 /***********************************************************************
961  *           DRIVE_Chdir
962  */
963 int DRIVE_Chdir( int drive, const char *path )
964 {
965     DOS_FULL_NAME full_name;
966     char buffer[MAX_PATHNAME_LEN];
967     LPSTR unix_cwd;
968     BY_HANDLE_FILE_INFORMATION info;
969     TDB *pTask = TASK_GetCurrent();
970
971     strcpy( buffer, "A:" );
972     buffer[0] += drive;
973     TRACE("(%s,%s)\n", buffer, path );
974     lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
975
976     if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
977     if (!FILE_Stat( full_name.long_name, &info )) return 0;
978     if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
979     {
980         SetLastError( ERROR_FILE_NOT_FOUND );
981         return 0;
982     }
983     unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
984     while (*unix_cwd == '/') unix_cwd++;
985
986     TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
987                    'A' + drive, unix_cwd, full_name.short_name + 3 );
988
989     HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
990     HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
991     DOSDrives[drive].dos_cwd  = heap_strdup( full_name.short_name + 3 );
992     DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
993
994     if (pTask && (pTask->curdrive & 0x80) && 
995         ((pTask->curdrive & ~0x80) == drive))
996     {
997         lstrcpynA( pTask->curdir, full_name.short_name + 2,
998                      sizeof(pTask->curdir) );
999         DRIVE_LastTask = GetCurrentTask();
1000         chdir(unix_cwd); /* Only change if on current drive */
1001     }
1002     return 1;
1003 }
1004
1005
1006 /***********************************************************************
1007  *           DRIVE_Disable
1008  */
1009 int DRIVE_Disable( int drive  )
1010 {
1011     if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1012     {
1013         SetLastError( ERROR_INVALID_DRIVE );
1014         return 0;
1015     }
1016     DOSDrives[drive].flags |= DRIVE_DISABLED;
1017     return 1;
1018 }
1019
1020
1021 /***********************************************************************
1022  *           DRIVE_Enable
1023  */
1024 int DRIVE_Enable( int drive  )
1025 {
1026     if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1027     {
1028         SetLastError( ERROR_INVALID_DRIVE );
1029         return 0;
1030     }
1031     DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1032     return 1;
1033 }
1034
1035
1036 /***********************************************************************
1037  *           DRIVE_SetLogicalMapping
1038  */
1039 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
1040 {
1041  /* If new_drive is already valid, do nothing and return 0
1042     otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1043   
1044     DOSDRIVE *old, *new;
1045     
1046     old = DOSDrives + existing_drive;
1047     new = DOSDrives + new_drive;
1048
1049     if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
1050         !old->root ||
1051         (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
1052     {
1053         SetLastError( ERROR_INVALID_DRIVE );
1054         return 0;
1055     }
1056
1057     if ( new->root )
1058     {
1059         TRACE("Can't map drive %c: to already existing drive %c:\n",
1060               'A' + existing_drive, 'A' + new_drive );
1061         /* it is already mapped there, so return success */
1062         if (!strcmp(old->root,new->root))
1063             return 1;
1064         return 0;
1065     }
1066
1067     new->root     = heap_strdup( old->root );
1068     new->dos_cwd  = heap_strdup( old->dos_cwd );
1069     new->unix_cwd = heap_strdup( old->unix_cwd );
1070     new->device   = heap_strdup( old->device );
1071     memcpy ( new->label_conf, old->label_conf, 12 );
1072     memcpy ( new->label_read, old->label_read, 12 );
1073     new->serial_conf = old->serial_conf;
1074     new->type = old->type;
1075     new->flags = old->flags;
1076     new->dev = old->dev;
1077     new->ino = old->ino;
1078
1079     TRACE("Drive %c: is now equal to drive %c:\n",
1080           'A' + new_drive, 'A' + existing_drive );
1081
1082     return 1;
1083 }
1084
1085
1086 /***********************************************************************
1087  *           DRIVE_OpenDevice
1088  *
1089  * Open the drive raw device and return a Unix fd (or -1 on error).
1090  */
1091 int DRIVE_OpenDevice( int drive, int flags )
1092 {
1093     if (!DRIVE_IsValid( drive )) return -1;
1094     return open( DOSDrives[drive].device, flags );
1095 }
1096
1097
1098 /***********************************************************************
1099  *           DRIVE_RawRead
1100  *
1101  * Read raw sectors from a device
1102  */
1103 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1104 {
1105     int fd;
1106
1107     if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1108     {
1109         lseek( fd, begin * 512, SEEK_SET );
1110         /* FIXME: check errors */
1111         read( fd, dataptr, nr_sect * 512 );
1112         close( fd );
1113     }
1114     else
1115     {
1116         memset(dataptr, 0, nr_sect * 512);
1117         if (fake_success)
1118         {
1119             if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
1120             if (begin == 1) *dataptr = 0xf8;
1121         }
1122         else
1123             return 0;
1124     }
1125     return 1;
1126 }
1127
1128
1129 /***********************************************************************
1130  *           DRIVE_RawWrite
1131  *
1132  * Write raw sectors to a device
1133  */
1134 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1135 {
1136     int fd;
1137
1138     if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1139     {
1140         lseek( fd, begin * 512, SEEK_SET );
1141         /* FIXME: check errors */
1142         write( fd, dataptr, nr_sect * 512 );
1143         close( fd );
1144     }
1145     else
1146     if (!(fake_success))
1147         return 0;
1148
1149     return 1;
1150 }
1151
1152
1153 /***********************************************************************
1154  *           DRIVE_GetFreeSpace
1155  */
1156 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size, 
1157                                PULARGE_INTEGER available )
1158 {
1159     struct statfs info;
1160
1161     if (!DRIVE_IsValid(drive))
1162     {
1163         SetLastError( ERROR_INVALID_DRIVE );
1164         return 0;
1165     }
1166
1167 /* FIXME: add autoconf check for this */
1168 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1169     if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
1170 #else
1171     if (statfs( DOSDrives[drive].root, &info) < 0)
1172 #endif
1173     {
1174         FILE_SetDosError();
1175         WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
1176         return 0;
1177     }
1178
1179     size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
1180 #ifdef STATFS_HAS_BAVAIL
1181     available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
1182 #else
1183 # ifdef STATFS_HAS_BFREE
1184     available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
1185 # else
1186 #  error "statfs has no bfree/bavail member!"
1187 # endif
1188 #endif
1189     if (DOSDrives[drive].type == DRIVE_CDROM)
1190     { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1191         available->QuadPart = 0;
1192     }
1193     return 1;
1194 }
1195
1196 /***********************************************************************
1197  *       DRIVE_GetCurrentDirectory
1198  * Returns "X:\\path\\etc\\".
1199  *
1200  * Despite the API description, return required length including the 
1201  * terminating null when buffer too small. This is the real behaviour.
1202 */
1203
1204 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
1205 {
1206     UINT ret;
1207     const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1208
1209     assert(s);
1210     ret = strlen(s) + 3; /* length of WHOLE current directory */
1211     if (ret >= buflen) return ret + 1;
1212     lstrcpynA( buf, "A:\\", min( 4u, buflen ) );
1213     if (buflen) buf[0] += DRIVE_GetCurrentDrive();
1214     if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
1215     return ret;
1216 }
1217
1218
1219 /***********************************************************************
1220  *           DRIVE_BuildEnv
1221  *
1222  * Build the environment array containing the drives' current directories.
1223  * Resulting pointer must be freed with HeapFree.
1224  */
1225 char *DRIVE_BuildEnv(void)
1226 {
1227     int i, length = 0;
1228     const char *cwd[MAX_DOS_DRIVES];
1229     char *env, *p;
1230
1231     for (i = 0; i < MAX_DOS_DRIVES; i++)
1232     {
1233         if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0]) length += strlen(cwd[i]) + 8;
1234     }
1235     if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
1236     for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
1237     {
1238         if (cwd[i] && cwd[i][0])
1239             p += sprintf( p, "=%c:=%c:\\%s", 'A'+i, 'A'+i, cwd[i] ) + 1;
1240     }
1241     *p = 0;
1242     return env;
1243 }
1244
1245
1246 /***********************************************************************
1247  *           GetDiskFreeSpace   (KERNEL.422)
1248  */
1249 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
1250                                   LPDWORD sector_bytes, LPDWORD free_clusters,
1251                                   LPDWORD total_clusters )
1252 {
1253     return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
1254                                 free_clusters, total_clusters );
1255 }
1256
1257
1258 /***********************************************************************
1259  *           GetDiskFreeSpaceA   (KERNEL32.@)
1260  *
1261  * Fails if expression resulting from current drive's dir and "root"
1262  * is not a root dir of the target drive.
1263  *
1264  * UNDOC: setting some LPDWORDs to NULL is perfectly possible 
1265  * if the corresponding info is unneeded.
1266  *
1267  * FIXME: needs to support UNC names from Win95 OSR2 on.
1268  *
1269  * Behaviour under Win95a:
1270  * CurrDir     root   result
1271  * "E:\\TEST"  "E:"   FALSE
1272  * "E:\\"      "E:"   TRUE
1273  * "E:\\"      "E"    FALSE
1274  * "E:\\"      "\\"   TRUE
1275  * "E:\\TEST"  "\\"   TRUE
1276  * "E:\\TEST"  ":\\"  FALSE
1277  * "E:\\TEST"  "E:\\" TRUE
1278  * "E:\\TEST"  ""     FALSE
1279  * "E:\\"      ""     FALSE (!)
1280  * "E:\\"      0x0    TRUE
1281  * "E:\\TEST"  0x0    TRUE  (!)
1282  * "E:\\TEST"  "C:"   TRUE  (when CurrDir of "C:" set to "\\")
1283  * "E:\\TEST"  "C:"   FALSE (when CurrDir of "C:" set to "\\TEST")
1284  */
1285 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1286                                    LPDWORD sector_bytes, LPDWORD free_clusters,
1287                                    LPDWORD total_clusters )
1288 {
1289     int drive, sec_size;
1290     ULARGE_INTEGER size,available;
1291     LPCSTR path;
1292     DWORD cluster_sec;
1293
1294     if ((!root) || (strcmp(root,"\\") == 0))
1295         drive = DRIVE_GetCurrentDrive();
1296     else
1297     if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
1298     {
1299         drive = toupper(root[0]) - 'A';
1300         path = &root[2];
1301         if (path[0] == '\0')
1302             path = DRIVE_GetDosCwd(drive);
1303         else
1304         if (path[0] == '\\')
1305             path++;
1306         if (path[0]) /* oops, we are in a subdir */
1307         {
1308             SetLastError(ERROR_INVALID_NAME);
1309             return FALSE;
1310         }
1311     }
1312     else
1313     {
1314         SetLastError(ERROR_INVALID_NAME);
1315         return FALSE;
1316     }
1317
1318     if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1319
1320     /* Cap the size and available at 2GB as per specs.  */
1321     if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1322     {
1323         size.s.HighPart = 0;
1324         size.s.LowPart = 0x7fffffff;
1325     }
1326     if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1327     {
1328         available.s.HighPart =0;
1329         available.s.LowPart = 0x7fffffff;
1330     }
1331     sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1332     size.s.LowPart            /= sec_size;
1333     available.s.LowPart       /= sec_size;
1334     /* FIXME: probably have to adjust those variables too for CDFS */
1335     cluster_sec = 1;
1336     while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1337
1338     if (cluster_sectors)
1339         *cluster_sectors = cluster_sec;
1340     if (sector_bytes)
1341         *sector_bytes    = sec_size;
1342     if (free_clusters)
1343         *free_clusters   = available.s.LowPart / cluster_sec;
1344     if (total_clusters)
1345         *total_clusters  = size.s.LowPart / cluster_sec;
1346     return TRUE;
1347 }
1348
1349
1350 /***********************************************************************
1351  *           GetDiskFreeSpaceW   (KERNEL32.@)
1352  */
1353 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1354                                    LPDWORD sector_bytes, LPDWORD free_clusters,
1355                                    LPDWORD total_clusters )
1356 {
1357     LPSTR xroot;
1358     BOOL ret;
1359
1360     xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1361     ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
1362                                free_clusters, total_clusters );
1363     HeapFree( GetProcessHeap(), 0, xroot );
1364     return ret;
1365 }
1366
1367
1368 /***********************************************************************
1369  *           GetDiskFreeSpaceExA   (KERNEL32.@)
1370  *
1371  *  This function is used to acquire the size of the available and
1372  *  total space on a logical volume.
1373  *
1374  * RETURNS
1375  *
1376  *  Zero on failure, nonzero upon success. Use GetLastError to obtain
1377  *  detailed error information.
1378  *
1379  */
1380 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1381                                      PULARGE_INTEGER avail,
1382                                      PULARGE_INTEGER total,
1383                                      PULARGE_INTEGER totalfree)
1384 {
1385     int drive;
1386     ULARGE_INTEGER size,available;
1387
1388     if (!root) drive = DRIVE_GetCurrentDrive();
1389     else
1390     { /* C: always works for GetDiskFreeSpaceEx */
1391         if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
1392         {
1393             FIXME("there are valid root names which are not supported yet\n");
1394             /* ..like UNC names, for instance. */
1395
1396             WARN("invalid root '%s'\n", root );
1397             return FALSE;
1398         }
1399         drive = toupper(root[0]) - 'A';
1400     }
1401
1402     if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1403
1404     if (total)
1405     {
1406         total->s.HighPart = size.s.HighPart;
1407         total->s.LowPart = size.s.LowPart;
1408     }
1409
1410     if (totalfree)
1411     {
1412         totalfree->s.HighPart = available.s.HighPart;
1413         totalfree->s.LowPart = available.s.LowPart;
1414     }
1415
1416     if (avail)
1417     {
1418         if (FIXME_ON(dosfs))
1419         {
1420             /* On Windows2000, we need to check the disk quota
1421                allocated for the user owning the calling process. We
1422                don't want to be more obtrusive than necessary with the
1423                FIXME messages, so don't print the FIXME unless Wine is
1424                actually masquerading as Windows2000. */
1425
1426             OSVERSIONINFOA ovi;
1427             ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1428             if (GetVersionExA(&ovi))
1429             {
1430               if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1431                   FIXME("no per-user quota support yet\n");
1432             }
1433         }
1434
1435         /* Quick hack, should eventually be fixed to work 100% with
1436            Windows2000 (see comment above). */
1437         avail->s.HighPart = available.s.HighPart;
1438         avail->s.LowPart = available.s.LowPart;
1439     }
1440
1441     return TRUE;
1442 }
1443
1444 /***********************************************************************
1445  *           GetDiskFreeSpaceExW   (KERNEL32.@)
1446  */
1447 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1448                                      PULARGE_INTEGER total,
1449                                      PULARGE_INTEGER  totalfree)
1450 {
1451     LPSTR xroot;
1452     BOOL ret;
1453
1454     xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1455     ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1456     HeapFree( GetProcessHeap(), 0, xroot );
1457     return ret;
1458 }
1459
1460 /***********************************************************************
1461  *           GetDriveType   (KERNEL.136)
1462  * This function returns the type of a drive in Win16. 
1463  * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1464  * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1465  * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1466  * do any pseudo-clever changes.
1467  *
1468  * RETURNS
1469  *      drivetype DRIVE_xxx
1470  */
1471 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1472 {
1473     UINT type = DRIVE_GetType(drive);
1474     TRACE("(%c:)\n", 'A' + drive );
1475     if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1476     return type;
1477 }
1478
1479
1480 /***********************************************************************
1481  *           GetDriveTypeA   (KERNEL32.@)
1482  *
1483  * Returns the type of the disk drive specified. If root is NULL the
1484  * root of the current directory is used.
1485  *
1486  * RETURNS
1487  *
1488  *  Type of drive (from Win32 SDK):
1489  *
1490  *   DRIVE_UNKNOWN     unable to find out anything about the drive
1491  *   DRIVE_NO_ROOT_DIR nonexistent root dir
1492  *   DRIVE_REMOVABLE   the disk can be removed from the machine
1493  *   DRIVE_FIXED       the disk can not be removed from the machine
1494  *   DRIVE_REMOTE      network disk
1495  *   DRIVE_CDROM       CDROM drive
1496  *   DRIVE_RAMDISK     virtual disk in RAM
1497  */
1498 UINT WINAPI GetDriveTypeA(LPCSTR root) /* [in] String describing drive */
1499 {
1500     int drive;
1501     TRACE("(%s)\n", debugstr_a(root));
1502
1503     if (NULL == root) drive = DRIVE_GetCurrentDrive();
1504     else
1505     {
1506         if ((root[1]) && (root[1] != ':'))
1507         {
1508             WARN("invalid root %s\n", debugstr_a(root));
1509             return DRIVE_NO_ROOT_DIR;
1510         }
1511         drive = toupper(root[0]) - 'A';
1512     }
1513     return DRIVE_GetType(drive);
1514 }
1515
1516
1517 /***********************************************************************
1518  *           GetDriveTypeW   (KERNEL32.@)
1519  */
1520 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1521 {
1522     LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1523     UINT ret = GetDriveTypeA( xpath );
1524     HeapFree( GetProcessHeap(), 0, xpath );
1525     return ret;
1526 }
1527
1528
1529 /***********************************************************************
1530  *           GetCurrentDirectory   (KERNEL.411)
1531  */
1532 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1533 {
1534     return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1535 }
1536
1537
1538 /***********************************************************************
1539  *           GetCurrentDirectoryA   (KERNEL32.@)
1540  */
1541 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1542 {
1543     UINT ret;
1544     char longname[MAX_PATHNAME_LEN];
1545     char shortname[MAX_PATHNAME_LEN];
1546     ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1547     if ( ret > MAX_PATHNAME_LEN ) {
1548       ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1549       return ret;
1550     }
1551     GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1552     ret = strlen( longname ) + 1;
1553     if (ret > buflen) return ret;
1554     strcpy(buf, longname);
1555     return ret - 1;
1556 }
1557
1558 /***********************************************************************
1559  *           GetCurrentDirectoryW   (KERNEL32.@)
1560  */
1561 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1562 {
1563     LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1564     UINT ret = GetCurrentDirectoryA( buflen, xpath );
1565     if (ret < buflen) ret = MultiByteToWideChar( CP_ACP, 0, xpath, -1, buf, buflen ) - 1;
1566     HeapFree( GetProcessHeap(), 0, xpath );
1567     return ret;
1568 }
1569
1570
1571 /***********************************************************************
1572  *           SetCurrentDirectory   (KERNEL.412)
1573  */
1574 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1575 {
1576     return SetCurrentDirectoryA( dir );
1577 }
1578
1579
1580 /***********************************************************************
1581  *           SetCurrentDirectoryA   (KERNEL32.@)
1582  */
1583 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1584 {
1585     int drive, olddrive = DRIVE_GetCurrentDrive();
1586
1587     if (!dir) {
1588         ERR_(file)("(NULL)!\n");
1589         return FALSE;
1590     }
1591     if (dir[0] && (dir[1]==':'))
1592     {
1593         drive = toupper( *dir ) - 'A';
1594         dir += 2;
1595     }
1596     else
1597         drive = olddrive;
1598
1599     /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1600        sets pTask->curdir only if pTask->curdrive is drive */
1601     if (!(DRIVE_SetCurrentDrive( drive )))
1602         return FALSE;
1603     /* FIXME: what about empty strings? Add a \\ ? */
1604     if (!DRIVE_Chdir( drive, dir )) {
1605         DRIVE_SetCurrentDrive(olddrive);
1606         return FALSE;
1607     }
1608     return TRUE;
1609 }
1610
1611
1612 /***********************************************************************
1613  *           SetCurrentDirectoryW   (KERNEL32.@)
1614  */
1615 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1616 {
1617     LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1618     BOOL res = SetCurrentDirectoryA( dir );
1619     HeapFree( GetProcessHeap(), 0, dir );
1620     return res;
1621 }
1622
1623
1624 /***********************************************************************
1625  *           GetLogicalDriveStringsA   (KERNEL32.@)
1626  */
1627 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1628 {
1629     int drive, count;
1630
1631     for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1632         if (DRIVE_IsValid(drive)) count++;
1633     if ((count * 4) + 1 <= len)
1634     {
1635         LPSTR p = buffer;
1636         for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1637             if (DRIVE_IsValid(drive))
1638             {
1639                 *p++ = 'a' + drive;
1640                 *p++ = ':';
1641                 *p++ = '\\';
1642                 *p++ = '\0';
1643             }
1644         *p = '\0';
1645         return count * 4;
1646     }
1647     else
1648         return (count * 4) + 1; /* account for terminating null */
1649     /* The API tells about these different return values */
1650 }
1651
1652
1653 /***********************************************************************
1654  *           GetLogicalDriveStringsW   (KERNEL32.@)
1655  */
1656 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1657 {
1658     int drive, count;
1659
1660     for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1661         if (DRIVE_IsValid(drive)) count++;
1662     if (count * 4 * sizeof(WCHAR) <= len)
1663     {
1664         LPWSTR p = buffer;
1665         for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1666             if (DRIVE_IsValid(drive))
1667             {
1668                 *p++ = (WCHAR)('a' + drive);
1669                 *p++ = (WCHAR)':';
1670                 *p++ = (WCHAR)'\\';
1671                 *p++ = (WCHAR)'\0';
1672             }
1673         *p = (WCHAR)'\0';
1674     }
1675     return count * 4 * sizeof(WCHAR);
1676 }
1677
1678
1679 /***********************************************************************
1680  *           GetLogicalDrives   (KERNEL32.@)
1681  */
1682 DWORD WINAPI GetLogicalDrives(void)
1683 {
1684     DWORD ret = 0;
1685     int drive;
1686
1687     for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1688     {
1689         if ( (DRIVE_IsValid(drive)) ||
1690             (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1691             ret |= (1 << drive);
1692     }
1693     return ret;
1694 }
1695
1696
1697 /***********************************************************************
1698  *           GetVolumeInformationA   (KERNEL32.@)
1699  */
1700 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1701                                        DWORD label_len, DWORD *serial,
1702                                        DWORD *filename_len, DWORD *flags,
1703                                        LPSTR fsname, DWORD fsname_len )
1704 {
1705     int drive;
1706     char *cp;
1707
1708     /* FIXME, SetLastError()s missing */
1709
1710     if (!root) drive = DRIVE_GetCurrentDrive();
1711     else
1712     {
1713         if ((root[1]) && (root[1] != ':'))
1714         {
1715             WARN("invalid root '%s'\n",root);
1716             return FALSE;
1717         }
1718         drive = toupper(root[0]) - 'A';
1719     }
1720     if (!DRIVE_IsValid( drive )) return FALSE;
1721     if (label)
1722     {
1723        lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1724        cp = label + strlen(label);
1725        while (cp != label && *(cp-1) == ' ') cp--;
1726        *cp = '\0';
1727     }
1728     if (serial) *serial = DRIVE_GetSerialNumber(drive);
1729
1730     /* Set the filesystem information */
1731     /* Note: we only emulate a FAT fs at present */
1732
1733     if (filename_len) {
1734         if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1735             *filename_len = 12;
1736         else
1737             *filename_len = 255;
1738     }
1739     if (flags)
1740       {
1741        *flags=0;
1742        if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1743          *flags|=FS_CASE_SENSITIVE;
1744        if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1745          *flags|=FS_CASE_IS_PRESERVED;
1746       }
1747     if (fsname) {
1748         /* Diablo checks that return code ... */
1749         if (DOSDrives[drive].type == DRIVE_CDROM)
1750             lstrcpynA( fsname, "CDFS", fsname_len );
1751         else
1752             lstrcpynA( fsname, "FAT", fsname_len );
1753     }
1754     return TRUE;
1755 }
1756
1757
1758 /***********************************************************************
1759  *           GetVolumeInformationW   (KERNEL32.@)
1760  */
1761 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1762                                        DWORD label_len, DWORD *serial,
1763                                        DWORD *filename_len, DWORD *flags,
1764                                        LPWSTR fsname, DWORD fsname_len )
1765 {
1766     LPSTR xroot    = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1767     LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1768     LPSTR xfsname  = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1769     BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1770                                           filename_len, flags, xfsname,
1771                                           fsname_len );
1772     if (ret)
1773     {
1774         if (label) MultiByteToWideChar( CP_ACP, 0, xvolname, -1, label, label_len );
1775         if (fsname) MultiByteToWideChar( CP_ACP, 0, xfsname, -1, fsname, fsname_len );
1776     }
1777     HeapFree( GetProcessHeap(), 0, xroot );
1778     HeapFree( GetProcessHeap(), 0, xvolname );
1779     HeapFree( GetProcessHeap(), 0, xfsname );
1780     return ret;
1781 }
1782
1783 /***********************************************************************
1784  *           SetVolumeLabelA   (KERNEL32.@)
1785  */
1786 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1787 {
1788     int drive;
1789     
1790     /* FIXME, SetLastErrors missing */
1791
1792     if (!root) drive = DRIVE_GetCurrentDrive();
1793     else
1794     {
1795         if ((root[1]) && (root[1] != ':'))
1796         {
1797             WARN("invalid root '%s'\n",root);
1798             return FALSE;
1799         }
1800         drive = toupper(root[0]) - 'A';
1801     }
1802     if (!DRIVE_IsValid( drive )) return FALSE;
1803
1804     /* some copy protection stuff check this */
1805     if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
1806
1807     FIXME("(%s,%s),stub!\n", root, volname);
1808     return TRUE;
1809 }
1810
1811 /***********************************************************************
1812  *           SetVolumeLabelW   (KERNEL32.@)
1813  */
1814 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1815 {
1816     LPSTR xroot, xvol;
1817     BOOL ret;
1818
1819     xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1820     xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1821     ret = SetVolumeLabelA( xroot, xvol );
1822     HeapFree( GetProcessHeap(), 0, xroot );
1823     HeapFree( GetProcessHeap(), 0, xvol );
1824     return ret;
1825 }