Release 20020509.
[wine/gsoc_dplay.git] / files / drive.c
blob2090ca099fbf7b81c154dbc2766cc20fbd05522f
1 /*
2 * DOS drives handling functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
7 * Label & serial number read support.
8 * (c) 1999 Petr Tomasek <tomasek@etf.cuni.cz>
9 * (c) 2000 Andreas Mohr (changes)
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.
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.
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
26 #include "config.h"
27 #include "wine/port.h"
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>
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
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"
71 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
72 WINE_DECLARE_DEBUG_CHANNEL(file);
74 typedef struct
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;
90 static const char * const DRIVE_Types[] =
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 */
102 /* Known filesystem types */
104 typedef struct
106 const char *name;
107 UINT flags;
108 } FS_DESCR;
110 static const FS_DESCR DRIVE_Filesystems[] =
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 }
122 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
123 static int DRIVE_CurDrive = -1;
125 static HTASK16 DRIVE_LastTask = 0;
127 /* strdup on the process heap */
128 inline static char *heap_strdup( const char *str )
130 INT len = strlen(str) + 1;
131 LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
132 if (p) memcpy( p, str, len );
133 return p;
136 extern void CDROM_InitRegistry(int dev);
138 /***********************************************************************
139 * DRIVE_GetDriveType
141 static UINT DRIVE_GetDriveType( const char *name )
143 char buffer[20];
144 int i;
146 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
147 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
149 if (!strcasecmp( buffer, DRIVE_Types[i] )) return i;
151 MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
152 name, buffer );
153 return DRIVE_FIXED;
157 /***********************************************************************
158 * DRIVE_GetFSFlags
160 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
162 const FS_DESCR *descr;
164 for (descr = DRIVE_Filesystems; descr->name; descr++)
165 if (!strcasecmp( value, descr->name )) return descr->flags;
166 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
167 name, value );
168 return DRIVE_CASE_PRESERVING;
172 /***********************************************************************
173 * DRIVE_Init
175 int DRIVE_Init(void)
177 int i, len, count = 0;
178 char name[] = "Drive A";
179 char drive_env[] = "=A:";
180 char path[MAX_PATHNAME_LEN];
181 char buffer[80];
182 struct stat drive_stat_buffer;
183 char *p;
184 DOSDRIVE *drive;
186 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
188 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
189 if (path[0])
191 p = path + strlen(path) - 1;
192 while ((p > path) && (*p == '/')) *p-- = '\0';
194 if (path[0] == '/')
196 drive->root = heap_strdup( path );
198 else
200 /* relative paths are relative to config dir */
201 const char *config = get_config_dir();
202 drive->root = HeapAlloc( GetProcessHeap(), 0, strlen(config) + strlen(path) + 2 );
203 sprintf( drive->root, "%s/%s", config, path );
206 if (stat( drive->root, &drive_stat_buffer ))
208 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
209 drive->root, strerror(errno), 'A' + i);
210 HeapFree( GetProcessHeap(), 0, drive->root );
211 drive->root = NULL;
212 continue;
214 if (!S_ISDIR(drive_stat_buffer.st_mode))
216 MESSAGE("%s is not a directory, ignoring drive %c:\n",
217 drive->root, 'A' + i );
218 HeapFree( GetProcessHeap(), 0, drive->root );
219 drive->root = NULL;
220 continue;
223 drive->dos_cwd = heap_strdup( "" );
224 drive->unix_cwd = heap_strdup( "" );
225 drive->type = DRIVE_GetDriveType( name );
226 drive->device = NULL;
227 drive->flags = 0;
228 drive->dev = drive_stat_buffer.st_dev;
229 drive->ino = drive_stat_buffer.st_ino;
231 /* Get the drive label */
232 PROFILE_GetWineIniString( name, "Label", "", drive->label_conf, 12 );
233 if ((len = strlen(drive->label_conf)) < 11)
235 /* Pad label with spaces */
236 memset( drive->label_conf + len, ' ', 11 - len );
237 drive->label_conf[11] = '\0';
240 /* Get the serial number */
241 PROFILE_GetWineIniString( name, "Serial", "12345678",
242 buffer, sizeof(buffer) );
243 drive->serial_conf = strtoul( buffer, NULL, 16 );
245 /* Get the filesystem type */
246 PROFILE_GetWineIniString( name, "Filesystem", "win95",
247 buffer, sizeof(buffer) );
248 drive->flags = DRIVE_GetFSFlags( name, buffer );
250 /* Get the device */
251 PROFILE_GetWineIniString( name, "Device", "",
252 buffer, sizeof(buffer) );
253 if (buffer[0])
255 int cd_fd;
256 drive->device = heap_strdup( buffer );
257 if (PROFILE_GetWineIniBool( name, "ReadVolInfo", 1))
258 drive->flags |= DRIVE_READ_VOL_INFO;
259 if (drive->type == DRIVE_CDROM)
261 if ((cd_fd = open(buffer,O_RDONLY|O_NONBLOCK)) != -1) {
262 CDROM_InitRegistry(cd_fd);
263 close(cd_fd);
268 /* Get the FailReadOnly flag */
269 if (PROFILE_GetWineIniBool( name, "FailReadOnly", 0 ))
270 drive->flags |= DRIVE_FAIL_READ_ONLY;
272 /* Make the first hard disk the current drive */
273 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
274 DRIVE_CurDrive = i;
276 count++;
277 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
278 "flags=%08x dev=%x ino=%x\n",
279 name, drive->root, DRIVE_Types[drive->type],
280 drive->label_conf, drive->serial_conf, drive->flags,
281 (int)drive->dev, (int)drive->ino );
283 else WARN("%s: not defined\n", name );
286 if (!count)
288 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
289 /* Create a C drive pointing to Unix root dir */
290 DOSDrives[2].root = heap_strdup( "/" );
291 DOSDrives[2].dos_cwd = heap_strdup( "" );
292 DOSDrives[2].unix_cwd = heap_strdup( "" );
293 strcpy( DOSDrives[2].label_conf, "Drive C " );
294 DOSDrives[2].serial_conf = 12345678;
295 DOSDrives[2].type = DRIVE_FIXED;
296 DOSDrives[2].device = NULL;
297 DOSDrives[2].flags = 0;
298 DRIVE_CurDrive = 2;
301 /* Make sure the current drive is valid */
302 if (DRIVE_CurDrive == -1)
304 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
306 if (drive->root && !(drive->flags & DRIVE_DISABLED))
308 DRIVE_CurDrive = i;
309 break;
314 /* get current working directory info for all drives */
315 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
317 if (!GetEnvironmentVariableA(drive_env, path, sizeof(path))) continue;
318 /* sanity check */
319 if (toupper(path[0]) != drive_env[1] || path[1] != ':') continue;
320 DRIVE_Chdir( i, path + 2 );
322 return 1;
326 /***********************************************************************
327 * DRIVE_IsValid
329 int DRIVE_IsValid( int drive )
331 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
332 return (DOSDrives[drive].root &&
333 !(DOSDrives[drive].flags & DRIVE_DISABLED));
337 /***********************************************************************
338 * DRIVE_GetCurrentDrive
340 int DRIVE_GetCurrentDrive(void)
342 TDB *pTask = TASK_GetCurrent();
343 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
344 return DRIVE_CurDrive;
348 /***********************************************************************
349 * DRIVE_SetCurrentDrive
351 int DRIVE_SetCurrentDrive( int drive )
353 TDB *pTask = TASK_GetCurrent();
354 if (!DRIVE_IsValid( drive ))
356 SetLastError( ERROR_INVALID_DRIVE );
357 return 0;
359 TRACE("%c:\n", 'A' + drive );
360 DRIVE_CurDrive = drive;
361 if (pTask) pTask->curdrive = drive | 0x80;
362 chdir(DRIVE_GetUnixCwd(drive));
363 return 1;
367 /***********************************************************************
368 * DRIVE_FindDriveRoot
370 * Find a drive for which the root matches the beginning of the given path.
371 * This can be used to translate a Unix path into a drive + DOS path.
372 * Return value is the drive, or -1 on error. On success, path is modified
373 * to point to the beginning of the DOS path.
375 int DRIVE_FindDriveRoot( const char **path )
377 /* Starting with the full path, check if the device and inode match any of
378 * the wine 'drives'. If not then remove the last path component and try
379 * again. If the last component was a '..' then skip a normal component
380 * since it's a directory that's ascended back out of.
382 int drive, level, len;
383 char buffer[MAX_PATHNAME_LEN];
384 char *p;
385 struct stat st;
387 strcpy( buffer, *path );
388 while ((p = strchr( buffer, '\\' )) != NULL)
389 *p = '/';
390 len = strlen(buffer);
392 while (len > 0)
394 /* Find the drive */
395 if (stat( buffer, &st ) == 0 && S_ISDIR( st.st_mode ))
397 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
399 if (!DOSDrives[drive].root ||
400 (DOSDrives[drive].flags & DRIVE_DISABLED))
401 continue;
403 if ((DOSDrives[drive].dev == st.st_dev) &&
404 (DOSDrives[drive].ino == st.st_ino))
406 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
407 *path, 'A' + drive, buffer, *path + len);
408 *path += len;
409 return drive;
414 level = 0;
415 while (len > 0 && level < 1)
417 /* strip off a trailing slash */
418 while (len > 0 && buffer[len - 1] == '/')
419 buffer[--len] = 0;
420 /* find start of the last path component */
421 while (len > 0 && buffer[len - 1] != '/')
422 --len;
423 /* does removing it take us up a level? */
424 if (strcmp( buffer + len, "." ) != 0)
425 level += strcmp( buffer + len, ".." ) ? 1 : -1;
426 buffer[len] = 0;
430 return -1;
434 /***********************************************************************
435 * DRIVE_GetRoot
437 const char * DRIVE_GetRoot( int drive )
439 if (!DRIVE_IsValid( drive )) return NULL;
440 return DOSDrives[drive].root;
444 /***********************************************************************
445 * DRIVE_GetDosCwd
447 const char * DRIVE_GetDosCwd( int drive )
449 TDB *pTask = TASK_GetCurrent();
450 if (!DRIVE_IsValid( drive )) return NULL;
452 /* Check if we need to change the directory to the new task. */
453 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
454 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
455 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
457 /* Perform the task-switch */
458 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
459 DRIVE_LastTask = GetCurrentTask();
461 return DOSDrives[drive].dos_cwd;
465 /***********************************************************************
466 * DRIVE_GetUnixCwd
468 const char * DRIVE_GetUnixCwd( int drive )
470 TDB *pTask = TASK_GetCurrent();
471 if (!DRIVE_IsValid( drive )) return NULL;
473 /* Check if we need to change the directory to the new task. */
474 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
475 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
476 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
478 /* Perform the task-switch */
479 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
480 DRIVE_LastTask = GetCurrentTask();
482 return DOSDrives[drive].unix_cwd;
486 /***********************************************************************
487 * DRIVE_GetDevice
489 const char * DRIVE_GetDevice( int drive )
491 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
494 /******************************************************************
495 * static WORD CDROM_Data_FindBestVoldesc
499 static WORD CDROM_Data_FindBestVoldesc(int fd)
501 BYTE cur_vd_type, max_vd_type = 0;
502 unsigned int offs, best_offs = 0, extra_offs = 0;
503 char sig[3];
505 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
507 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
508 * the volume label is displaced forward by 8
510 lseek(fd, offs + 11, SEEK_SET); /* check for non-ISO9660 signature */
511 read(fd, &sig, 3);
512 if ((sig[0] == 'R') && (sig[1] == 'O') && (sig[2]=='M'))
514 extra_offs = 8;
516 lseek(fd, offs + extra_offs, SEEK_SET);
517 read(fd, &cur_vd_type, 1);
518 if (cur_vd_type == 0xff) /* voldesc set terminator */
519 break;
520 if (cur_vd_type > max_vd_type)
522 max_vd_type = cur_vd_type;
523 best_offs = offs + extra_offs;
526 return best_offs;
529 /***********************************************************************
530 * DRIVE_ReadSuperblock
532 * NOTE
533 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
534 * to check, that they are writing on a FAT filesystem !
536 int DRIVE_ReadSuperblock (int drive, char * buff)
538 #define DRIVE_SUPER 96
539 int fd;
540 off_t offs;
541 int ret = 0;
543 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
544 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
546 struct stat st;
547 if (!DOSDrives[drive].device)
548 ERR("No device configured for drive %c: !\n", 'A'+drive);
549 else
550 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
551 (stat(DOSDrives[drive].device, &st)) ?
552 "not available or symlink not valid ?" : "no permission");
553 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
554 PROFILE_UsageWineIni();
555 return -1;
558 switch(DOSDrives[drive].type)
560 case DRIVE_REMOVABLE:
561 case DRIVE_FIXED:
562 offs = 0;
563 break;
564 case DRIVE_CDROM:
565 offs = CDROM_Data_FindBestVoldesc(fd);
566 break;
567 default:
568 offs = 0;
569 break;
572 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs))
574 ret = -4;
575 goto the_end;
577 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER)
579 ret = -2;
580 goto the_end;
583 switch(DOSDrives[drive].type)
585 case DRIVE_REMOVABLE:
586 case DRIVE_FIXED:
587 if ((buff[0x26]!=0x29) || /* Check for FAT present */
588 /* FIXME: do really all FAT have their name beginning with
589 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
590 memcmp( buff+0x36,"FAT",3))
592 ERR("The filesystem is not FAT !! (device=%s)\n",
593 DOSDrives[drive].device);
594 ret = -3;
595 goto the_end;
597 break;
598 case DRIVE_CDROM:
599 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
601 ret = -3;
602 goto the_end;
604 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
605 break;
606 default:
607 ret = -3;
608 goto the_end;
611 return close(fd);
612 the_end:
613 close(fd);
614 return ret;
618 /***********************************************************************
619 * DRIVE_WriteSuperblockEntry
621 * NOTE
622 * We are writing as little as possible (ie. not the whole SuperBlock)
623 * not to interfere with kernel. The drive can be mounted !
625 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
627 int fd;
629 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
631 ERR("Cannot open the device %s (for writing)\n",
632 DOSDrives[drive].device);
633 return -1;
635 if (lseek(fd,ofs,SEEK_SET)!=ofs)
637 ERR("lseek failed on device %s !\n",
638 DOSDrives[drive].device);
639 close(fd);
640 return -2;
642 if (write(fd,buff,len)!=len)
644 close(fd);
645 ERR("Cannot write on %s !\n",
646 DOSDrives[drive].device);
647 return -3;
649 return close (fd);
652 /******************************************************************
653 * static HANDLE CDROM_Open
657 static HANDLE CDROM_Open(int drive)
659 char root[6];
661 strcpy(root, "\\\\.\\A:");
662 root[4] += drive;
664 return CreateFileA(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
667 /**************************************************************************
668 * CDROM_Data_GetLabel [internal]
670 DWORD CDROM_Data_GetLabel(int drive, char *label)
672 #define LABEL_LEN 32+1
673 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
674 WORD offs = CDROM_Data_FindBestVoldesc(dev);
675 WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
676 DWORD unicode_id = 0;
678 if (offs)
680 if ((lseek(dev, offs+0x58, SEEK_SET) == offs+0x58)
681 && (read(dev, &unicode_id, 3) == 3))
683 int ver = (unicode_id & 0xff0000) >> 16;
685 if ((lseek(dev, offs+0x28, SEEK_SET) != offs+0x28)
686 || (read(dev, &label_read, LABEL_LEN) != LABEL_LEN))
687 goto failure;
689 close(dev);
690 if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
691 && ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
692 { /* yippee, unicode */
693 int i;
694 WORD ch;
695 for (i=0; i<LABEL_LEN;i++)
696 { /* Motorola -> Intel Unicode conversion :-\ */
697 ch = label_read[i];
698 label_read[i] = (ch << 8) | (ch >> 8);
700 WideCharToMultiByte( CP_ACP, 0, label_read, -1, label, 12, NULL, NULL );
701 label[11] = 0;
703 else
705 strncpy(label, (LPSTR)label_read, 11);
706 label[11] = '\0';
708 return 1;
711 failure:
712 close(dev);
713 ERR("error reading label !\n");
714 return 0;
717 /**************************************************************************
718 * CDROM_GetLabel [internal]
720 static DWORD CDROM_GetLabel(int drive, char *label)
722 HANDLE h = CDROM_Open(drive);
723 CDROM_DISK_DATA cdd;
724 DWORD br;
725 DWORD ret = 1;
727 if (!h || !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
728 return 0;
730 switch (cdd.DiskData & 0x03)
732 case CDROM_DISK_DATA_TRACK:
733 if (!CDROM_Data_GetLabel(drive, label))
734 ret = 0;
735 break;
736 case CDROM_DISK_AUDIO_TRACK:
737 strcpy(label, "Audio CD ");
738 break;
739 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
740 FIXME("Need to get the label of a mixed mode CD: not implemented yet !\n");
741 /* fall through */
742 case 0:
743 ret = 0;
744 break;
746 TRACE("CD: label is '%s'.\n", label);
748 return ret;
750 /***********************************************************************
751 * DRIVE_GetLabel
753 const char * DRIVE_GetLabel( int drive )
755 int read = 0;
756 char buff[DRIVE_SUPER];
757 int offs = -1;
759 if (!DRIVE_IsValid( drive )) return NULL;
760 if (DOSDrives[drive].type == DRIVE_CDROM)
762 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
764 else
765 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
767 if (DRIVE_ReadSuperblock(drive,(char *) buff))
768 ERR("Invalid or unreadable superblock on %s (%c:).\n",
769 DOSDrives[drive].device, (char)(drive+'A'));
770 else {
771 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
772 DOSDrives[drive].type == DRIVE_FIXED)
773 offs = 0x2b;
775 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
776 if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
777 DOSDrives[drive].label_read[11]='\0';
778 read = 1;
782 return (read) ?
783 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
786 #define CDFRAMES_PERSEC 75
787 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
788 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
789 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
791 /**************************************************************************
792 * CDROM_Audio_GetSerial [internal]
794 static DWORD CDROM_Audio_GetSerial(HANDLE h)
796 unsigned long serial = 0;
797 int i;
798 WORD wMagic;
799 DWORD dwStart, dwEnd, br;
800 CDROM_TOC toc;
802 if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
803 return 0;
806 * wMagic collects the wFrames from track 1
807 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
808 * frames.
809 * There it is collected for correcting the serial when there are less than
810 * 3 tracks.
812 wMagic = toc.TrackData[0].Address[2];
813 dwStart = FRAME_OF_TOC(toc, toc.FirstTrack);
815 for (i = 0; i <= toc.LastTrack - toc.FirstTrack; i++) {
816 serial += (toc.TrackData[i].Address[0] << 16) |
817 (toc.TrackData[i].Address[1] << 8) | toc.TrackData[i].Address[2];
819 dwEnd = FRAME_OF_TOC(toc, toc.LastTrack + 1);
821 if (toc.LastTrack - toc.FirstTrack + 1 < 3)
822 serial += wMagic + (dwEnd - dwStart);
824 return serial;
827 /**************************************************************************
828 * CDROM_Data_GetSerial [internal]
830 static DWORD CDROM_Data_GetSerial(int drive)
832 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
833 WORD offs;
834 union {
835 unsigned long val;
836 unsigned char p[4];
837 } serial;
838 BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
841 if (dev == -1) return 0;
842 offs = CDROM_Data_FindBestVoldesc(dev);
844 serial.val = 0;
845 if (offs)
847 BYTE buf[2048];
848 OSVERSIONINFOA ovi;
849 int i;
851 lseek(dev, offs, SEEK_SET);
852 read(dev, buf, 2048);
854 * OK, another braindead one... argh. Just believe it.
855 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
856 * It's true and nobody will ever be able to change it.
858 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
859 GetVersionExA(&ovi);
860 if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (ovi.dwMajorVersion >= 4))
862 b0 = 3; b1 = 2; b2 = 1; b3 = 0;
864 for (i = 0; i < 2048; i += 4)
866 /* DON'T optimize this into DWORD !! (breaks overflow) */
867 serial.p[b0] += buf[i+b0];
868 serial.p[b1] += buf[i+b1];
869 serial.p[b2] += buf[i+b2];
870 serial.p[b3] += buf[i+b3];
873 close(dev);
874 return serial.val;
877 /**************************************************************************
878 * CDROM_GetSerial [internal]
880 static DWORD CDROM_GetSerial(int drive)
882 DWORD serial = 0;
883 HANDLE h = CDROM_Open(drive);
884 CDROM_DISK_DATA cdd;
885 DWORD br;
887 if (!h || ! !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
888 return 0;
890 switch (cdd.DiskData & 0x03)
892 case CDROM_DISK_DATA_TRACK:
893 /* hopefully a data CD */
894 serial = CDROM_Data_GetSerial(drive);
895 break;
896 case CDROM_DISK_AUDIO_TRACK:
897 /* fall thru */
898 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
899 serial = CDROM_Audio_GetSerial(h);
900 break;
901 case 0:
902 break;
905 if (serial)
906 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
908 CloseHandle(h);
910 return serial;
913 /***********************************************************************
914 * DRIVE_GetSerialNumber
916 DWORD DRIVE_GetSerialNumber( int drive )
918 DWORD serial = 0;
919 char buff[DRIVE_SUPER];
921 if (!DRIVE_IsValid( drive )) return 0;
923 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
925 switch(DOSDrives[drive].type)
927 case DRIVE_REMOVABLE:
928 case DRIVE_FIXED:
929 if (DRIVE_ReadSuperblock(drive,(char *) buff))
930 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
931 " Maybe not FAT?\n" ,
932 DOSDrives[drive].device, 'A'+drive);
933 else
934 serial = *((DWORD*)(buff+0x27));
935 break;
936 case DRIVE_CDROM:
937 serial = CDROM_GetSerial(drive);
938 break;
939 default:
940 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
944 return (serial) ? serial : DOSDrives[drive].serial_conf;
948 /***********************************************************************
949 * DRIVE_SetSerialNumber
951 int DRIVE_SetSerialNumber( int drive, DWORD serial )
953 char buff[DRIVE_SUPER];
955 if (!DRIVE_IsValid( drive )) return 0;
957 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
959 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
960 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
961 /* check, if the drive has a FAT filesystem */
962 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
963 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
964 return 1;
967 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
968 DOSDrives[drive].serial_conf = serial;
969 return 1;
973 /***********************************************************************
974 * DRIVE_GetType
976 static UINT DRIVE_GetType( int drive )
978 if (!DRIVE_IsValid( drive )) return DRIVE_UNKNOWN;
979 return DOSDrives[drive].type;
983 /***********************************************************************
984 * DRIVE_GetFlags
986 UINT DRIVE_GetFlags( int drive )
988 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
989 return DOSDrives[drive].flags;
993 /***********************************************************************
994 * DRIVE_Chdir
996 int DRIVE_Chdir( int drive, const char *path )
998 DOS_FULL_NAME full_name;
999 char buffer[MAX_PATHNAME_LEN];
1000 LPSTR unix_cwd;
1001 BY_HANDLE_FILE_INFORMATION info;
1002 TDB *pTask = TASK_GetCurrent();
1004 strcpy( buffer, "A:" );
1005 buffer[0] += drive;
1006 TRACE("(%s,%s)\n", buffer, path );
1007 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
1009 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
1010 if (!FILE_Stat( full_name.long_name, &info )) return 0;
1011 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1013 SetLastError( ERROR_FILE_NOT_FOUND );
1014 return 0;
1016 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
1017 while (*unix_cwd == '/') unix_cwd++;
1019 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1020 'A' + drive, unix_cwd, full_name.short_name + 3 );
1022 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
1023 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
1024 DOSDrives[drive].dos_cwd = heap_strdup( full_name.short_name + 3 );
1025 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
1027 if (pTask && (pTask->curdrive & 0x80) &&
1028 ((pTask->curdrive & ~0x80) == drive))
1030 lstrcpynA( pTask->curdir, full_name.short_name + 2,
1031 sizeof(pTask->curdir) );
1032 DRIVE_LastTask = GetCurrentTask();
1033 chdir(unix_cwd); /* Only change if on current drive */
1035 return 1;
1039 /***********************************************************************
1040 * DRIVE_Disable
1042 int DRIVE_Disable( int drive )
1044 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1046 SetLastError( ERROR_INVALID_DRIVE );
1047 return 0;
1049 DOSDrives[drive].flags |= DRIVE_DISABLED;
1050 return 1;
1054 /***********************************************************************
1055 * DRIVE_Enable
1057 int DRIVE_Enable( int drive )
1059 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1061 SetLastError( ERROR_INVALID_DRIVE );
1062 return 0;
1064 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1065 return 1;
1069 /***********************************************************************
1070 * DRIVE_SetLogicalMapping
1072 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
1074 /* If new_drive is already valid, do nothing and return 0
1075 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1077 DOSDRIVE *old, *new;
1079 old = DOSDrives + existing_drive;
1080 new = DOSDrives + new_drive;
1082 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
1083 !old->root ||
1084 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
1086 SetLastError( ERROR_INVALID_DRIVE );
1087 return 0;
1090 if ( new->root )
1092 TRACE("Can't map drive %c: to already existing drive %c:\n",
1093 'A' + existing_drive, 'A' + new_drive );
1094 /* it is already mapped there, so return success */
1095 if (!strcmp(old->root,new->root))
1096 return 1;
1097 return 0;
1100 new->root = heap_strdup( old->root );
1101 new->dos_cwd = heap_strdup( old->dos_cwd );
1102 new->unix_cwd = heap_strdup( old->unix_cwd );
1103 new->device = heap_strdup( old->device );
1104 memcpy ( new->label_conf, old->label_conf, 12 );
1105 memcpy ( new->label_read, old->label_read, 12 );
1106 new->serial_conf = old->serial_conf;
1107 new->type = old->type;
1108 new->flags = old->flags;
1109 new->dev = old->dev;
1110 new->ino = old->ino;
1112 TRACE("Drive %c: is now equal to drive %c:\n",
1113 'A' + new_drive, 'A' + existing_drive );
1115 return 1;
1119 /***********************************************************************
1120 * DRIVE_OpenDevice
1122 * Open the drive raw device and return a Unix fd (or -1 on error).
1124 int DRIVE_OpenDevice( int drive, int flags )
1126 if (!DRIVE_IsValid( drive )) return -1;
1127 return open( DOSDrives[drive].device, flags );
1131 /***********************************************************************
1132 * DRIVE_RawRead
1134 * Read raw sectors from a device
1136 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1138 int fd;
1140 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1142 lseek( fd, begin * 512, SEEK_SET );
1143 /* FIXME: check errors */
1144 read( fd, dataptr, nr_sect * 512 );
1145 close( fd );
1147 else
1149 memset(dataptr, 0, nr_sect * 512);
1150 if (fake_success)
1152 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
1153 if (begin == 1) *dataptr = 0xf8;
1155 else
1156 return 0;
1158 return 1;
1162 /***********************************************************************
1163 * DRIVE_RawWrite
1165 * Write raw sectors to a device
1167 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1169 int fd;
1171 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1173 lseek( fd, begin * 512, SEEK_SET );
1174 /* FIXME: check errors */
1175 write( fd, dataptr, nr_sect * 512 );
1176 close( fd );
1178 else
1179 if (!(fake_success))
1180 return 0;
1182 return 1;
1186 /***********************************************************************
1187 * DRIVE_GetFreeSpace
1189 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
1190 PULARGE_INTEGER available )
1192 struct statfs info;
1194 if (!DRIVE_IsValid(drive))
1196 SetLastError( ERROR_INVALID_DRIVE );
1197 return 0;
1200 /* FIXME: add autoconf check for this */
1201 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1202 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
1203 #else
1204 if (statfs( DOSDrives[drive].root, &info) < 0)
1205 #endif
1207 FILE_SetDosError();
1208 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
1209 return 0;
1212 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
1213 #ifdef STATFS_HAS_BAVAIL
1214 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
1215 #else
1216 # ifdef STATFS_HAS_BFREE
1217 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
1218 # else
1219 # error "statfs has no bfree/bavail member!"
1220 # endif
1221 #endif
1222 if (DOSDrives[drive].type == DRIVE_CDROM)
1223 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1224 available->QuadPart = 0;
1226 return 1;
1229 /***********************************************************************
1230 * DRIVE_GetCurrentDirectory
1231 * Returns "X:\\path\\etc\\".
1233 * Despite the API description, return required length including the
1234 * terminating null when buffer too small. This is the real behaviour.
1237 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
1239 UINT ret;
1240 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1242 assert(s);
1243 ret = strlen(s) + 3; /* length of WHOLE current directory */
1244 if (ret >= buflen) return ret + 1;
1245 lstrcpynA( buf, "A:\\", min( 4u, buflen ) );
1246 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
1247 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
1248 return ret;
1252 /***********************************************************************
1253 * DRIVE_BuildEnv
1255 * Build the environment array containing the drives' current directories.
1256 * Resulting pointer must be freed with HeapFree.
1258 char *DRIVE_BuildEnv(void)
1260 int i, length = 0;
1261 const char *cwd[MAX_DOS_DRIVES];
1262 char *env, *p;
1264 for (i = 0; i < MAX_DOS_DRIVES; i++)
1266 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0]) length += strlen(cwd[i]) + 8;
1268 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
1269 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
1271 if (cwd[i] && cwd[i][0])
1272 p += sprintf( p, "=%c:=%c:\\%s", 'A'+i, 'A'+i, cwd[i] ) + 1;
1274 *p = 0;
1275 return env;
1279 /***********************************************************************
1280 * GetDiskFreeSpace (KERNEL.422)
1282 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
1283 LPDWORD sector_bytes, LPDWORD free_clusters,
1284 LPDWORD total_clusters )
1286 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
1287 free_clusters, total_clusters );
1291 /***********************************************************************
1292 * GetDiskFreeSpaceA (KERNEL32.@)
1294 * Fails if expression resulting from current drive's dir and "root"
1295 * is not a root dir of the target drive.
1297 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1298 * if the corresponding info is unneeded.
1300 * FIXME: needs to support UNC names from Win95 OSR2 on.
1302 * Behaviour under Win95a:
1303 * CurrDir root result
1304 * "E:\\TEST" "E:" FALSE
1305 * "E:\\" "E:" TRUE
1306 * "E:\\" "E" FALSE
1307 * "E:\\" "\\" TRUE
1308 * "E:\\TEST" "\\" TRUE
1309 * "E:\\TEST" ":\\" FALSE
1310 * "E:\\TEST" "E:\\" TRUE
1311 * "E:\\TEST" "" FALSE
1312 * "E:\\" "" FALSE (!)
1313 * "E:\\" 0x0 TRUE
1314 * "E:\\TEST" 0x0 TRUE (!)
1315 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1316 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1318 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1319 LPDWORD sector_bytes, LPDWORD free_clusters,
1320 LPDWORD total_clusters )
1322 int drive, sec_size;
1323 ULARGE_INTEGER size,available;
1324 LPCSTR path;
1325 DWORD cluster_sec;
1327 if ((!root) || (strcmp(root,"\\") == 0))
1328 drive = DRIVE_GetCurrentDrive();
1329 else
1330 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
1332 drive = toupper(root[0]) - 'A';
1333 path = &root[2];
1334 if (path[0] == '\0')
1335 path = DRIVE_GetDosCwd(drive);
1336 else
1337 if (path[0] == '\\')
1338 path++;
1339 if (path[0]) /* oops, we are in a subdir */
1341 SetLastError(ERROR_INVALID_NAME);
1342 return FALSE;
1345 else
1347 SetLastError(ERROR_INVALID_NAME);
1348 return FALSE;
1351 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1353 /* Cap the size and available at 2GB as per specs. */
1354 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1356 size.s.HighPart = 0;
1357 size.s.LowPart = 0x7fffffff;
1359 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1361 available.s.HighPart =0;
1362 available.s.LowPart = 0x7fffffff;
1364 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1365 size.s.LowPart /= sec_size;
1366 available.s.LowPart /= sec_size;
1367 /* FIXME: probably have to adjust those variables too for CDFS */
1368 cluster_sec = 1;
1369 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1371 if (cluster_sectors)
1372 *cluster_sectors = cluster_sec;
1373 if (sector_bytes)
1374 *sector_bytes = sec_size;
1375 if (free_clusters)
1376 *free_clusters = available.s.LowPart / cluster_sec;
1377 if (total_clusters)
1378 *total_clusters = size.s.LowPart / cluster_sec;
1379 return TRUE;
1383 /***********************************************************************
1384 * GetDiskFreeSpaceW (KERNEL32.@)
1386 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1387 LPDWORD sector_bytes, LPDWORD free_clusters,
1388 LPDWORD total_clusters )
1390 LPSTR xroot;
1391 BOOL ret;
1393 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1394 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
1395 free_clusters, total_clusters );
1396 HeapFree( GetProcessHeap(), 0, xroot );
1397 return ret;
1401 /***********************************************************************
1402 * GetDiskFreeSpaceExA (KERNEL32.@)
1404 * This function is used to acquire the size of the available and
1405 * total space on a logical volume.
1407 * RETURNS
1409 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1410 * detailed error information.
1413 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1414 PULARGE_INTEGER avail,
1415 PULARGE_INTEGER total,
1416 PULARGE_INTEGER totalfree)
1418 int drive;
1419 ULARGE_INTEGER size,available;
1421 if (!root) drive = DRIVE_GetCurrentDrive();
1422 else
1423 { /* C: always works for GetDiskFreeSpaceEx */
1424 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
1426 FIXME("there are valid root names which are not supported yet\n");
1427 /* ..like UNC names, for instance. */
1429 WARN("invalid root '%s'\n", root );
1430 return FALSE;
1432 drive = toupper(root[0]) - 'A';
1435 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1437 if (total)
1439 total->s.HighPart = size.s.HighPart;
1440 total->s.LowPart = size.s.LowPart;
1443 if (totalfree)
1445 totalfree->s.HighPart = available.s.HighPart;
1446 totalfree->s.LowPart = available.s.LowPart;
1449 if (avail)
1451 if (FIXME_ON(dosfs))
1453 /* On Windows2000, we need to check the disk quota
1454 allocated for the user owning the calling process. We
1455 don't want to be more obtrusive than necessary with the
1456 FIXME messages, so don't print the FIXME unless Wine is
1457 actually masquerading as Windows2000. */
1459 OSVERSIONINFOA ovi;
1460 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1461 if (GetVersionExA(&ovi))
1463 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1464 FIXME("no per-user quota support yet\n");
1468 /* Quick hack, should eventually be fixed to work 100% with
1469 Windows2000 (see comment above). */
1470 avail->s.HighPart = available.s.HighPart;
1471 avail->s.LowPart = available.s.LowPart;
1474 return TRUE;
1477 /***********************************************************************
1478 * GetDiskFreeSpaceExW (KERNEL32.@)
1480 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1481 PULARGE_INTEGER total,
1482 PULARGE_INTEGER totalfree)
1484 LPSTR xroot;
1485 BOOL ret;
1487 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1488 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1489 HeapFree( GetProcessHeap(), 0, xroot );
1490 return ret;
1493 /***********************************************************************
1494 * GetDriveType (KERNEL.136)
1495 * This function returns the type of a drive in Win16.
1496 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1497 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1498 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1499 * do any pseudo-clever changes.
1501 * RETURNS
1502 * drivetype DRIVE_xxx
1504 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1506 UINT type = DRIVE_GetType(drive);
1507 TRACE("(%c:)\n", 'A' + drive );
1508 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1509 return type;
1513 /***********************************************************************
1514 * GetDriveTypeA (KERNEL32.@)
1516 * Returns the type of the disk drive specified. If root is NULL the
1517 * root of the current directory is used.
1519 * RETURNS
1521 * Type of drive (from Win32 SDK):
1523 * DRIVE_UNKNOWN unable to find out anything about the drive
1524 * DRIVE_NO_ROOT_DIR nonexistent root dir
1525 * DRIVE_REMOVABLE the disk can be removed from the machine
1526 * DRIVE_FIXED the disk can not be removed from the machine
1527 * DRIVE_REMOTE network disk
1528 * DRIVE_CDROM CDROM drive
1529 * DRIVE_RAMDISK virtual disk in RAM
1531 UINT WINAPI GetDriveTypeA(LPCSTR root) /* [in] String describing drive */
1533 int drive;
1534 TRACE("(%s)\n", debugstr_a(root));
1536 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1537 else
1539 if ((root[1]) && (root[1] != ':'))
1541 WARN("invalid root %s\n", debugstr_a(root));
1542 return DRIVE_NO_ROOT_DIR;
1544 drive = toupper(root[0]) - 'A';
1546 return DRIVE_GetType(drive);
1550 /***********************************************************************
1551 * GetDriveTypeW (KERNEL32.@)
1553 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1555 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1556 UINT ret = GetDriveTypeA( xpath );
1557 HeapFree( GetProcessHeap(), 0, xpath );
1558 return ret;
1562 /***********************************************************************
1563 * GetCurrentDirectory (KERNEL.411)
1565 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1567 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1571 /***********************************************************************
1572 * GetCurrentDirectoryA (KERNEL32.@)
1574 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1576 UINT ret;
1577 char longname[MAX_PATHNAME_LEN];
1578 char shortname[MAX_PATHNAME_LEN];
1579 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1580 if ( ret > MAX_PATHNAME_LEN ) {
1581 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1582 return ret;
1584 GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1585 ret = strlen( longname ) + 1;
1586 if (ret > buflen) return ret;
1587 strcpy(buf, longname);
1588 return ret - 1;
1591 /***********************************************************************
1592 * GetCurrentDirectoryW (KERNEL32.@)
1594 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1596 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1597 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1598 if (ret < buflen) ret = MultiByteToWideChar( CP_ACP, 0, xpath, -1, buf, buflen ) - 1;
1599 HeapFree( GetProcessHeap(), 0, xpath );
1600 return ret;
1604 /***********************************************************************
1605 * SetCurrentDirectory (KERNEL.412)
1607 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1609 return SetCurrentDirectoryA( dir );
1613 /***********************************************************************
1614 * SetCurrentDirectoryA (KERNEL32.@)
1616 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1618 int drive, olddrive = DRIVE_GetCurrentDrive();
1620 if (!dir) {
1621 ERR_(file)("(NULL)!\n");
1622 return FALSE;
1624 if (dir[0] && (dir[1]==':'))
1626 drive = toupper( *dir ) - 'A';
1627 dir += 2;
1629 else
1630 drive = olddrive;
1632 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1633 sets pTask->curdir only if pTask->curdrive is drive */
1634 if (!(DRIVE_SetCurrentDrive( drive )))
1635 return FALSE;
1636 /* FIXME: what about empty strings? Add a \\ ? */
1637 if (!DRIVE_Chdir( drive, dir )) {
1638 DRIVE_SetCurrentDrive(olddrive);
1639 return FALSE;
1641 return TRUE;
1645 /***********************************************************************
1646 * SetCurrentDirectoryW (KERNEL32.@)
1648 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1650 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1651 BOOL res = SetCurrentDirectoryA( dir );
1652 HeapFree( GetProcessHeap(), 0, dir );
1653 return res;
1657 /***********************************************************************
1658 * GetLogicalDriveStringsA (KERNEL32.@)
1660 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1662 int drive, count;
1664 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1665 if (DRIVE_IsValid(drive)) count++;
1666 if ((count * 4) + 1 <= len)
1668 LPSTR p = buffer;
1669 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1670 if (DRIVE_IsValid(drive))
1672 *p++ = 'a' + drive;
1673 *p++ = ':';
1674 *p++ = '\\';
1675 *p++ = '\0';
1677 *p = '\0';
1678 return count * 4;
1680 else
1681 return (count * 4) + 1; /* account for terminating null */
1682 /* The API tells about these different return values */
1686 /***********************************************************************
1687 * GetLogicalDriveStringsW (KERNEL32.@)
1689 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1691 int drive, count;
1693 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1694 if (DRIVE_IsValid(drive)) count++;
1695 if (count * 4 * sizeof(WCHAR) <= len)
1697 LPWSTR p = buffer;
1698 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1699 if (DRIVE_IsValid(drive))
1701 *p++ = (WCHAR)('a' + drive);
1702 *p++ = (WCHAR)':';
1703 *p++ = (WCHAR)'\\';
1704 *p++ = (WCHAR)'\0';
1706 *p = (WCHAR)'\0';
1708 return count * 4 * sizeof(WCHAR);
1712 /***********************************************************************
1713 * GetLogicalDrives (KERNEL32.@)
1715 DWORD WINAPI GetLogicalDrives(void)
1717 DWORD ret = 0;
1718 int drive;
1720 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1722 if ( (DRIVE_IsValid(drive)) ||
1723 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1724 ret |= (1 << drive);
1726 return ret;
1730 /***********************************************************************
1731 * GetVolumeInformationA (KERNEL32.@)
1733 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1734 DWORD label_len, DWORD *serial,
1735 DWORD *filename_len, DWORD *flags,
1736 LPSTR fsname, DWORD fsname_len )
1738 int drive;
1739 char *cp;
1741 /* FIXME, SetLastError()s missing */
1743 if (!root) drive = DRIVE_GetCurrentDrive();
1744 else
1746 if ((root[1]) && (root[1] != ':'))
1748 WARN("invalid root '%s'\n",root);
1749 return FALSE;
1751 drive = toupper(root[0]) - 'A';
1753 if (!DRIVE_IsValid( drive )) return FALSE;
1754 if (label)
1756 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1757 cp = label + strlen(label);
1758 while (cp != label && *(cp-1) == ' ') cp--;
1759 *cp = '\0';
1761 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1763 /* Set the filesystem information */
1764 /* Note: we only emulate a FAT fs at present */
1766 if (filename_len) {
1767 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1768 *filename_len = 12;
1769 else
1770 *filename_len = 255;
1772 if (flags)
1774 *flags=0;
1775 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1776 *flags|=FS_CASE_SENSITIVE;
1777 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1778 *flags|=FS_CASE_IS_PRESERVED;
1780 if (fsname) {
1781 /* Diablo checks that return code ... */
1782 if (DOSDrives[drive].type == DRIVE_CDROM)
1783 lstrcpynA( fsname, "CDFS", fsname_len );
1784 else
1785 lstrcpynA( fsname, "FAT", fsname_len );
1787 return TRUE;
1791 /***********************************************************************
1792 * GetVolumeInformationW (KERNEL32.@)
1794 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1795 DWORD label_len, DWORD *serial,
1796 DWORD *filename_len, DWORD *flags,
1797 LPWSTR fsname, DWORD fsname_len )
1799 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1800 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1801 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1802 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1803 filename_len, flags, xfsname,
1804 fsname_len );
1805 if (ret)
1807 if (label) MultiByteToWideChar( CP_ACP, 0, xvolname, -1, label, label_len );
1808 if (fsname) MultiByteToWideChar( CP_ACP, 0, xfsname, -1, fsname, fsname_len );
1810 HeapFree( GetProcessHeap(), 0, xroot );
1811 HeapFree( GetProcessHeap(), 0, xvolname );
1812 HeapFree( GetProcessHeap(), 0, xfsname );
1813 return ret;
1816 /***********************************************************************
1817 * SetVolumeLabelA (KERNEL32.@)
1819 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1821 int drive;
1823 /* FIXME, SetLastErrors missing */
1825 if (!root) drive = DRIVE_GetCurrentDrive();
1826 else
1828 if ((root[1]) && (root[1] != ':'))
1830 WARN("invalid root '%s'\n",root);
1831 return FALSE;
1833 drive = toupper(root[0]) - 'A';
1835 if (!DRIVE_IsValid( drive )) return FALSE;
1837 /* some copy protection stuff check this */
1838 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
1840 FIXME("(%s,%s),stub!\n", root, volname);
1841 return TRUE;
1844 /***********************************************************************
1845 * SetVolumeLabelW (KERNEL32.@)
1847 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1849 LPSTR xroot, xvol;
1850 BOOL ret;
1852 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1853 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1854 ret = SetVolumeLabelA( xroot, xvol );
1855 HeapFree( GetProcessHeap(), 0, xroot );
1856 HeapFree( GetProcessHeap(), 0, xvol );
1857 return ret;