Removed most inclusions of options.h.
[wine.git] / files / drive.c
blobd4736b4ec98682b910656efd44c4d84765a6b33d
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 "task.h"
64 #include "wine/debug.h"
65 #include "wine/server.h"
66 #include "winioctl.h"
67 #include "ntddstor.h"
68 #include "ntddcdrm.h"
70 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
71 WINE_DECLARE_DEBUG_CHANNEL(file);
73 typedef struct
75 char *root; /* root dir in Unix format without trailing / */
76 char *dos_cwd; /* cwd in DOS format without leading or trailing \ */
77 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
78 char *device; /* raw device path */
79 char label_conf[12]; /* drive label as cfg'd in wine config */
80 char label_read[12]; /* drive label as read from device */
81 DWORD serial_conf; /* drive serial number as cfg'd in wine config */
82 UINT type; /* drive type */
83 UINT flags; /* drive flags */
84 dev_t dev; /* unix device number */
85 ino_t ino; /* unix inode number */
86 } DOSDRIVE;
89 static const char * const DRIVE_Types[] =
91 "", /* DRIVE_UNKNOWN */
92 "", /* DRIVE_NO_ROOT_DIR */
93 "floppy", /* DRIVE_REMOVABLE */
94 "hd", /* DRIVE_FIXED */
95 "network", /* DRIVE_REMOTE */
96 "cdrom", /* DRIVE_CDROM */
97 "ramdisk" /* DRIVE_RAMDISK */
101 /* Known filesystem types */
103 typedef struct
105 const char *name;
106 UINT flags;
107 } FS_DESCR;
109 static const FS_DESCR DRIVE_Filesystems[] =
111 { "unix", DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
112 { "msdos", DRIVE_SHORT_NAMES },
113 { "dos", DRIVE_SHORT_NAMES },
114 { "fat", DRIVE_SHORT_NAMES },
115 { "vfat", DRIVE_CASE_PRESERVING },
116 { "win95", DRIVE_CASE_PRESERVING },
117 { NULL, 0 }
121 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
122 static int DRIVE_CurDrive = -1;
124 static HTASK16 DRIVE_LastTask = 0;
126 /* strdup on the process heap */
127 inline static char *heap_strdup( const char *str )
129 INT len = strlen(str) + 1;
130 LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
131 if (p) memcpy( p, str, len );
132 return p;
135 extern void CDROM_InitRegistry(int dev);
137 /***********************************************************************
138 * DRIVE_GetDriveType
140 static UINT DRIVE_GetDriveType( const char *name )
142 char buffer[20];
143 int i;
145 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
146 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
148 if (!strcasecmp( buffer, DRIVE_Types[i] )) return i;
150 MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
151 name, buffer );
152 return DRIVE_FIXED;
156 /***********************************************************************
157 * DRIVE_GetFSFlags
159 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
161 const FS_DESCR *descr;
163 for (descr = DRIVE_Filesystems; descr->name; descr++)
164 if (!strcasecmp( value, descr->name )) return descr->flags;
165 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
166 name, value );
167 return DRIVE_CASE_PRESERVING;
171 /***********************************************************************
172 * DRIVE_Init
174 int DRIVE_Init(void)
176 int i, len, count = 0;
177 char name[] = "Drive A";
178 char drive_env[] = "=A:";
179 char path[MAX_PATHNAME_LEN];
180 char buffer[80];
181 struct stat drive_stat_buffer;
182 char *p;
183 DOSDRIVE *drive;
185 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
187 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
188 if (path[0])
190 p = path + strlen(path) - 1;
191 while ((p > path) && (*p == '/')) *p-- = '\0';
193 if (path[0] == '/')
195 drive->root = heap_strdup( path );
197 else
199 /* relative paths are relative to config dir */
200 const char *config = get_config_dir();
201 drive->root = HeapAlloc( GetProcessHeap(), 0, strlen(config) + strlen(path) + 2 );
202 sprintf( drive->root, "%s/%s", config, path );
205 if (stat( drive->root, &drive_stat_buffer ))
207 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
208 drive->root, strerror(errno), 'A' + i);
209 HeapFree( GetProcessHeap(), 0, drive->root );
210 drive->root = NULL;
211 continue;
213 if (!S_ISDIR(drive_stat_buffer.st_mode))
215 MESSAGE("%s is not a directory, ignoring drive %c:\n",
216 drive->root, 'A' + i );
217 HeapFree( GetProcessHeap(), 0, drive->root );
218 drive->root = NULL;
219 continue;
222 drive->dos_cwd = heap_strdup( "" );
223 drive->unix_cwd = heap_strdup( "" );
224 drive->type = DRIVE_GetDriveType( name );
225 drive->device = NULL;
226 drive->flags = 0;
227 drive->dev = drive_stat_buffer.st_dev;
228 drive->ino = drive_stat_buffer.st_ino;
230 /* Get the drive label */
231 PROFILE_GetWineIniString( name, "Label", "", drive->label_conf, 12 );
232 if ((len = strlen(drive->label_conf)) < 11)
234 /* Pad label with spaces */
235 memset( drive->label_conf + len, ' ', 11 - len );
236 drive->label_conf[11] = '\0';
239 /* Get the serial number */
240 PROFILE_GetWineIniString( name, "Serial", "12345678",
241 buffer, sizeof(buffer) );
242 drive->serial_conf = strtoul( buffer, NULL, 16 );
244 /* Get the filesystem type */
245 PROFILE_GetWineIniString( name, "Filesystem", "win95",
246 buffer, sizeof(buffer) );
247 drive->flags = DRIVE_GetFSFlags( name, buffer );
249 /* Get the device */
250 PROFILE_GetWineIniString( name, "Device", "",
251 buffer, sizeof(buffer) );
252 if (buffer[0])
254 int cd_fd;
255 drive->device = heap_strdup( buffer );
256 if (PROFILE_GetWineIniBool( name, "ReadVolInfo", 1))
257 drive->flags |= DRIVE_READ_VOL_INFO;
258 if (drive->type == DRIVE_CDROM)
260 if ((cd_fd = open(buffer,O_RDONLY|O_NONBLOCK)) != -1) {
261 CDROM_InitRegistry(cd_fd);
262 close(cd_fd);
267 /* Get the FailReadOnly flag */
268 if (PROFILE_GetWineIniBool( name, "FailReadOnly", 0 ))
269 drive->flags |= DRIVE_FAIL_READ_ONLY;
271 /* Make the first hard disk the current drive */
272 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
273 DRIVE_CurDrive = i;
275 count++;
276 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
277 "flags=%08x dev=%x ino=%x\n",
278 name, drive->root, DRIVE_Types[drive->type],
279 drive->label_conf, drive->serial_conf, drive->flags,
280 (int)drive->dev, (int)drive->ino );
282 else WARN("%s: not defined\n", name );
285 if (!count)
287 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
288 /* Create a C drive pointing to Unix root dir */
289 DOSDrives[2].root = heap_strdup( "/" );
290 DOSDrives[2].dos_cwd = heap_strdup( "" );
291 DOSDrives[2].unix_cwd = heap_strdup( "" );
292 strcpy( DOSDrives[2].label_conf, "Drive C " );
293 DOSDrives[2].serial_conf = 12345678;
294 DOSDrives[2].type = DRIVE_FIXED;
295 DOSDrives[2].device = NULL;
296 DOSDrives[2].flags = 0;
297 DRIVE_CurDrive = 2;
300 /* Make sure the current drive is valid */
301 if (DRIVE_CurDrive == -1)
303 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
305 if (drive->root && !(drive->flags & DRIVE_DISABLED))
307 DRIVE_CurDrive = i;
308 break;
313 /* get current working directory info for all drives */
314 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
316 if (!GetEnvironmentVariableA(drive_env, path, sizeof(path))) continue;
317 /* sanity check */
318 if (toupper(path[0]) != drive_env[1] || path[1] != ':') continue;
319 DRIVE_Chdir( i, path + 2 );
321 return 1;
325 /***********************************************************************
326 * DRIVE_IsValid
328 int DRIVE_IsValid( int drive )
330 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
331 return (DOSDrives[drive].root &&
332 !(DOSDrives[drive].flags & DRIVE_DISABLED));
336 /***********************************************************************
337 * DRIVE_GetCurrentDrive
339 int DRIVE_GetCurrentDrive(void)
341 TDB *pTask = TASK_GetCurrent();
342 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
343 return DRIVE_CurDrive;
347 /***********************************************************************
348 * DRIVE_SetCurrentDrive
350 int DRIVE_SetCurrentDrive( int drive )
352 TDB *pTask = TASK_GetCurrent();
353 if (!DRIVE_IsValid( drive ))
355 SetLastError( ERROR_INVALID_DRIVE );
356 return 0;
358 TRACE("%c:\n", 'A' + drive );
359 DRIVE_CurDrive = drive;
360 if (pTask) pTask->curdrive = drive | 0x80;
361 chdir(DRIVE_GetUnixCwd(drive));
362 return 1;
366 /***********************************************************************
367 * DRIVE_FindDriveRoot
369 * Find a drive for which the root matches the beginning of the given path.
370 * This can be used to translate a Unix path into a drive + DOS path.
371 * Return value is the drive, or -1 on error. On success, path is modified
372 * to point to the beginning of the DOS path.
374 int DRIVE_FindDriveRoot( const char **path )
376 /* Starting with the full path, check if the device and inode match any of
377 * the wine 'drives'. If not then remove the last path component and try
378 * again. If the last component was a '..' then skip a normal component
379 * since it's a directory that's ascended back out of.
381 int drive, level, len;
382 char buffer[MAX_PATHNAME_LEN];
383 char *p;
384 struct stat st;
386 strcpy( buffer, *path );
387 while ((p = strchr( buffer, '\\' )) != NULL)
388 *p = '/';
389 len = strlen(buffer);
391 while (len > 0)
393 /* Find the drive */
394 if (stat( buffer, &st ) == 0 && S_ISDIR( st.st_mode ))
396 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
398 if (!DOSDrives[drive].root ||
399 (DOSDrives[drive].flags & DRIVE_DISABLED))
400 continue;
402 if ((DOSDrives[drive].dev == st.st_dev) &&
403 (DOSDrives[drive].ino == st.st_ino))
405 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
406 *path, 'A' + drive, buffer, *path + len);
407 *path += len;
408 return drive;
413 level = 0;
414 while (len > 0 && level < 1)
416 /* strip off a trailing slash */
417 while (len > 0 && buffer[len - 1] == '/')
418 buffer[--len] = 0;
419 /* find start of the last path component */
420 while (len > 0 && buffer[len - 1] != '/')
421 --len;
422 /* does removing it take us up a level? */
423 if (strcmp( buffer + len, "." ) != 0)
424 level += strcmp( buffer + len, ".." ) ? 1 : -1;
425 buffer[len] = 0;
429 return -1;
433 /***********************************************************************
434 * DRIVE_GetRoot
436 const char * DRIVE_GetRoot( int drive )
438 if (!DRIVE_IsValid( drive )) return NULL;
439 return DOSDrives[drive].root;
443 /***********************************************************************
444 * DRIVE_GetDosCwd
446 const char * DRIVE_GetDosCwd( int drive )
448 TDB *pTask = TASK_GetCurrent();
449 if (!DRIVE_IsValid( drive )) return NULL;
451 /* Check if we need to change the directory to the new task. */
452 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
453 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
454 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
456 /* Perform the task-switch */
457 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
458 DRIVE_LastTask = GetCurrentTask();
460 return DOSDrives[drive].dos_cwd;
464 /***********************************************************************
465 * DRIVE_GetUnixCwd
467 const char * DRIVE_GetUnixCwd( int drive )
469 TDB *pTask = TASK_GetCurrent();
470 if (!DRIVE_IsValid( drive )) return NULL;
472 /* Check if we need to change the directory to the new task. */
473 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
474 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
475 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
477 /* Perform the task-switch */
478 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
479 DRIVE_LastTask = GetCurrentTask();
481 return DOSDrives[drive].unix_cwd;
485 /***********************************************************************
486 * DRIVE_GetDevice
488 const char * DRIVE_GetDevice( int drive )
490 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
493 /******************************************************************
494 * static WORD CDROM_Data_FindBestVoldesc
498 static WORD CDROM_Data_FindBestVoldesc(int fd)
500 BYTE cur_vd_type, max_vd_type = 0;
501 unsigned int offs, best_offs = 0, extra_offs = 0;
502 char sig[3];
504 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
506 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
507 * the volume label is displaced forward by 8
509 lseek(fd, offs + 11, SEEK_SET); /* check for non-ISO9660 signature */
510 read(fd, &sig, 3);
511 if ((sig[0] == 'R') && (sig[1] == 'O') && (sig[2]=='M'))
513 extra_offs = 8;
515 lseek(fd, offs + extra_offs, SEEK_SET);
516 read(fd, &cur_vd_type, 1);
517 if (cur_vd_type == 0xff) /* voldesc set terminator */
518 break;
519 if (cur_vd_type > max_vd_type)
521 max_vd_type = cur_vd_type;
522 best_offs = offs + extra_offs;
525 return best_offs;
528 /***********************************************************************
529 * DRIVE_ReadSuperblock
531 * NOTE
532 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
533 * to check, that they are writing on a FAT filesystem !
535 int DRIVE_ReadSuperblock (int drive, char * buff)
537 #define DRIVE_SUPER 96
538 int fd;
539 off_t offs;
540 int ret = 0;
542 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
543 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
545 struct stat st;
546 if (!DOSDrives[drive].device)
547 ERR("No device configured for drive %c: !\n", 'A'+drive);
548 else
549 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
550 (stat(DOSDrives[drive].device, &st)) ?
551 "not available or symlink not valid ?" : "no permission");
552 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
553 PROFILE_UsageWineIni();
554 return -1;
557 switch(DOSDrives[drive].type)
559 case DRIVE_REMOVABLE:
560 case DRIVE_FIXED:
561 offs = 0;
562 break;
563 case DRIVE_CDROM:
564 offs = CDROM_Data_FindBestVoldesc(fd);
565 break;
566 default:
567 offs = 0;
568 break;
571 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs))
573 ret = -4;
574 goto the_end;
576 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER)
578 ret = -2;
579 goto the_end;
582 switch(DOSDrives[drive].type)
584 case DRIVE_REMOVABLE:
585 case DRIVE_FIXED:
586 if ((buff[0x26]!=0x29) || /* Check for FAT present */
587 /* FIXME: do really all FAT have their name beginning with
588 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
589 memcmp( buff+0x36,"FAT",3))
591 ERR("The filesystem is not FAT !! (device=%s)\n",
592 DOSDrives[drive].device);
593 ret = -3;
594 goto the_end;
596 break;
597 case DRIVE_CDROM:
598 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
600 ret = -3;
601 goto the_end;
603 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
604 break;
605 default:
606 ret = -3;
607 goto the_end;
610 return close(fd);
611 the_end:
612 close(fd);
613 return ret;
617 /***********************************************************************
618 * DRIVE_WriteSuperblockEntry
620 * NOTE
621 * We are writing as little as possible (ie. not the whole SuperBlock)
622 * not to interfere with kernel. The drive can be mounted !
624 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
626 int fd;
628 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
630 ERR("Cannot open the device %s (for writing)\n",
631 DOSDrives[drive].device);
632 return -1;
634 if (lseek(fd,ofs,SEEK_SET)!=ofs)
636 ERR("lseek failed on device %s !\n",
637 DOSDrives[drive].device);
638 close(fd);
639 return -2;
641 if (write(fd,buff,len)!=len)
643 close(fd);
644 ERR("Cannot write on %s !\n",
645 DOSDrives[drive].device);
646 return -3;
648 return close (fd);
651 /******************************************************************
652 * static HANDLE CDROM_Open
656 static HANDLE CDROM_Open(int drive)
658 char root[6];
660 strcpy(root, "\\\\.\\A:");
661 root[4] += drive;
663 return CreateFileA(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
666 /**************************************************************************
667 * CDROM_Data_GetLabel [internal]
669 DWORD CDROM_Data_GetLabel(int drive, char *label)
671 #define LABEL_LEN 32+1
672 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
673 WORD offs = CDROM_Data_FindBestVoldesc(dev);
674 WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
675 DWORD unicode_id = 0;
677 if (offs)
679 if ((lseek(dev, offs+0x58, SEEK_SET) == offs+0x58)
680 && (read(dev, &unicode_id, 3) == 3))
682 int ver = (unicode_id & 0xff0000) >> 16;
684 if ((lseek(dev, offs+0x28, SEEK_SET) != offs+0x28)
685 || (read(dev, &label_read, LABEL_LEN) != LABEL_LEN))
686 goto failure;
688 close(dev);
689 if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
690 && ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
691 { /* yippee, unicode */
692 int i;
693 WORD ch;
694 for (i=0; i<LABEL_LEN;i++)
695 { /* Motorola -> Intel Unicode conversion :-\ */
696 ch = label_read[i];
697 label_read[i] = (ch << 8) | (ch >> 8);
699 WideCharToMultiByte( CP_ACP, 0, label_read, -1, label, 12, NULL, NULL );
700 label[11] = 0;
702 else
704 strncpy(label, (LPSTR)label_read, 11);
705 label[11] = '\0';
707 return 1;
710 failure:
711 close(dev);
712 ERR("error reading label !\n");
713 return 0;
716 /**************************************************************************
717 * CDROM_GetLabel [internal]
719 static DWORD CDROM_GetLabel(int drive, char *label)
721 HANDLE h = CDROM_Open(drive);
722 CDROM_DISK_DATA cdd;
723 DWORD br;
724 DWORD ret = 1;
726 if (!h || !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
727 return 0;
729 switch (cdd.DiskData & 0x03)
731 case CDROM_DISK_DATA_TRACK:
732 if (!CDROM_Data_GetLabel(drive, label))
733 ret = 0;
734 break;
735 case CDROM_DISK_AUDIO_TRACK:
736 strcpy(label, "Audio CD ");
737 break;
738 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
739 FIXME("Need to get the label of a mixed mode CD: not implemented yet !\n");
740 /* fall through */
741 case 0:
742 ret = 0;
743 break;
745 TRACE("CD: label is '%s'.\n", label);
747 return ret;
749 /***********************************************************************
750 * DRIVE_GetLabel
752 const char * DRIVE_GetLabel( int drive )
754 int read = 0;
755 char buff[DRIVE_SUPER];
756 int offs = -1;
758 if (!DRIVE_IsValid( drive )) return NULL;
759 if (DOSDrives[drive].type == DRIVE_CDROM)
761 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
763 else
764 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
766 if (DRIVE_ReadSuperblock(drive,(char *) buff))
767 ERR("Invalid or unreadable superblock on %s (%c:).\n",
768 DOSDrives[drive].device, (char)(drive+'A'));
769 else {
770 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
771 DOSDrives[drive].type == DRIVE_FIXED)
772 offs = 0x2b;
774 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
775 if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
776 DOSDrives[drive].label_read[11]='\0';
777 read = 1;
781 return (read) ?
782 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
785 #define CDFRAMES_PERSEC 75
786 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
787 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
788 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
790 /**************************************************************************
791 * CDROM_Audio_GetSerial [internal]
793 static DWORD CDROM_Audio_GetSerial(HANDLE h)
795 unsigned long serial = 0;
796 int i;
797 WORD wMagic;
798 DWORD dwStart, dwEnd, br;
799 CDROM_TOC toc;
801 if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
802 return 0;
805 * wMagic collects the wFrames from track 1
806 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
807 * frames.
808 * There it is collected for correcting the serial when there are less than
809 * 3 tracks.
811 wMagic = toc.TrackData[0].Address[2];
812 dwStart = FRAME_OF_TOC(toc, toc.FirstTrack);
814 for (i = 0; i <= toc.LastTrack - toc.FirstTrack; i++) {
815 serial += (toc.TrackData[i].Address[0] << 16) |
816 (toc.TrackData[i].Address[1] << 8) | toc.TrackData[i].Address[2];
818 dwEnd = FRAME_OF_TOC(toc, toc.LastTrack + 1);
820 if (toc.LastTrack - toc.FirstTrack + 1 < 3)
821 serial += wMagic + (dwEnd - dwStart);
823 return serial;
826 /**************************************************************************
827 * CDROM_Data_GetSerial [internal]
829 static DWORD CDROM_Data_GetSerial(int drive)
831 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
832 WORD offs;
833 union {
834 unsigned long val;
835 unsigned char p[4];
836 } serial;
837 BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
840 if (dev == -1) return 0;
841 offs = CDROM_Data_FindBestVoldesc(dev);
843 serial.val = 0;
844 if (offs)
846 BYTE buf[2048];
847 OSVERSIONINFOA ovi;
848 int i;
850 lseek(dev, offs, SEEK_SET);
851 read(dev, buf, 2048);
853 * OK, another braindead one... argh. Just believe it.
854 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
855 * It's true and nobody will ever be able to change it.
857 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
858 GetVersionExA(&ovi);
859 if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (ovi.dwMajorVersion >= 4))
861 b0 = 3; b1 = 2; b2 = 1; b3 = 0;
863 for (i = 0; i < 2048; i += 4)
865 /* DON'T optimize this into DWORD !! (breaks overflow) */
866 serial.p[b0] += buf[i+b0];
867 serial.p[b1] += buf[i+b1];
868 serial.p[b2] += buf[i+b2];
869 serial.p[b3] += buf[i+b3];
872 close(dev);
873 return serial.val;
876 /**************************************************************************
877 * CDROM_GetSerial [internal]
879 static DWORD CDROM_GetSerial(int drive)
881 DWORD serial = 0;
882 HANDLE h = CDROM_Open(drive);
883 CDROM_DISK_DATA cdd;
884 DWORD br;
886 if (!h || ! !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
887 return 0;
889 switch (cdd.DiskData & 0x03)
891 case CDROM_DISK_DATA_TRACK:
892 /* hopefully a data CD */
893 serial = CDROM_Data_GetSerial(drive);
894 break;
895 case CDROM_DISK_AUDIO_TRACK:
896 /* fall thru */
897 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
898 serial = CDROM_Audio_GetSerial(h);
899 break;
900 case 0:
901 break;
904 if (serial)
905 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
907 CloseHandle(h);
909 return serial;
912 /***********************************************************************
913 * DRIVE_GetSerialNumber
915 DWORD DRIVE_GetSerialNumber( int drive )
917 DWORD serial = 0;
918 char buff[DRIVE_SUPER];
920 if (!DRIVE_IsValid( drive )) return 0;
922 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
924 switch(DOSDrives[drive].type)
926 case DRIVE_REMOVABLE:
927 case DRIVE_FIXED:
928 if (DRIVE_ReadSuperblock(drive,(char *) buff))
929 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
930 " Maybe not FAT?\n" ,
931 DOSDrives[drive].device, 'A'+drive);
932 else
933 serial = *((DWORD*)(buff+0x27));
934 break;
935 case DRIVE_CDROM:
936 serial = CDROM_GetSerial(drive);
937 break;
938 default:
939 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
943 return (serial) ? serial : DOSDrives[drive].serial_conf;
947 /***********************************************************************
948 * DRIVE_SetSerialNumber
950 int DRIVE_SetSerialNumber( int drive, DWORD serial )
952 char buff[DRIVE_SUPER];
954 if (!DRIVE_IsValid( drive )) return 0;
956 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
958 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
959 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
960 /* check, if the drive has a FAT filesystem */
961 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
962 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
963 return 1;
966 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
967 DOSDrives[drive].serial_conf = serial;
968 return 1;
972 /***********************************************************************
973 * DRIVE_GetType
975 static UINT DRIVE_GetType( int drive )
977 if (!DRIVE_IsValid( drive )) return DRIVE_UNKNOWN;
978 return DOSDrives[drive].type;
982 /***********************************************************************
983 * DRIVE_GetFlags
985 UINT DRIVE_GetFlags( int drive )
987 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
988 return DOSDrives[drive].flags;
992 /***********************************************************************
993 * DRIVE_Chdir
995 int DRIVE_Chdir( int drive, const char *path )
997 DOS_FULL_NAME full_name;
998 char buffer[MAX_PATHNAME_LEN];
999 LPSTR unix_cwd;
1000 BY_HANDLE_FILE_INFORMATION info;
1001 TDB *pTask = TASK_GetCurrent();
1003 strcpy( buffer, "A:" );
1004 buffer[0] += drive;
1005 TRACE("(%s,%s)\n", buffer, path );
1006 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
1008 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
1009 if (!FILE_Stat( full_name.long_name, &info )) return 0;
1010 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1012 SetLastError( ERROR_FILE_NOT_FOUND );
1013 return 0;
1015 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
1016 while (*unix_cwd == '/') unix_cwd++;
1018 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1019 'A' + drive, unix_cwd, full_name.short_name + 3 );
1021 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
1022 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
1023 DOSDrives[drive].dos_cwd = heap_strdup( full_name.short_name + 3 );
1024 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
1026 if (pTask && (pTask->curdrive & 0x80) &&
1027 ((pTask->curdrive & ~0x80) == drive))
1029 lstrcpynA( pTask->curdir, full_name.short_name + 2,
1030 sizeof(pTask->curdir) );
1031 DRIVE_LastTask = GetCurrentTask();
1032 chdir(unix_cwd); /* Only change if on current drive */
1034 return 1;
1038 /***********************************************************************
1039 * DRIVE_Disable
1041 int DRIVE_Disable( int drive )
1043 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1045 SetLastError( ERROR_INVALID_DRIVE );
1046 return 0;
1048 DOSDrives[drive].flags |= DRIVE_DISABLED;
1049 return 1;
1053 /***********************************************************************
1054 * DRIVE_Enable
1056 int DRIVE_Enable( int drive )
1058 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1060 SetLastError( ERROR_INVALID_DRIVE );
1061 return 0;
1063 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1064 return 1;
1068 /***********************************************************************
1069 * DRIVE_SetLogicalMapping
1071 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
1073 /* If new_drive is already valid, do nothing and return 0
1074 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1076 DOSDRIVE *old, *new;
1078 old = DOSDrives + existing_drive;
1079 new = DOSDrives + new_drive;
1081 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
1082 !old->root ||
1083 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
1085 SetLastError( ERROR_INVALID_DRIVE );
1086 return 0;
1089 if ( new->root )
1091 TRACE("Can't map drive %c: to already existing drive %c:\n",
1092 'A' + existing_drive, 'A' + new_drive );
1093 /* it is already mapped there, so return success */
1094 if (!strcmp(old->root,new->root))
1095 return 1;
1096 return 0;
1099 new->root = heap_strdup( old->root );
1100 new->dos_cwd = heap_strdup( old->dos_cwd );
1101 new->unix_cwd = heap_strdup( old->unix_cwd );
1102 new->device = heap_strdup( old->device );
1103 memcpy ( new->label_conf, old->label_conf, 12 );
1104 memcpy ( new->label_read, old->label_read, 12 );
1105 new->serial_conf = old->serial_conf;
1106 new->type = old->type;
1107 new->flags = old->flags;
1108 new->dev = old->dev;
1109 new->ino = old->ino;
1111 TRACE("Drive %c: is now equal to drive %c:\n",
1112 'A' + new_drive, 'A' + existing_drive );
1114 return 1;
1118 /***********************************************************************
1119 * DRIVE_OpenDevice
1121 * Open the drive raw device and return a Unix fd (or -1 on error).
1123 int DRIVE_OpenDevice( int drive, int flags )
1125 if (!DRIVE_IsValid( drive )) return -1;
1126 return open( DOSDrives[drive].device, flags );
1130 /***********************************************************************
1131 * DRIVE_RawRead
1133 * Read raw sectors from a device
1135 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1137 int fd;
1139 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1141 lseek( fd, begin * 512, SEEK_SET );
1142 /* FIXME: check errors */
1143 read( fd, dataptr, nr_sect * 512 );
1144 close( fd );
1146 else
1148 memset(dataptr, 0, nr_sect * 512);
1149 if (fake_success)
1151 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
1152 if (begin == 1) *dataptr = 0xf8;
1154 else
1155 return 0;
1157 return 1;
1161 /***********************************************************************
1162 * DRIVE_RawWrite
1164 * Write raw sectors to a device
1166 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1168 int fd;
1170 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1172 lseek( fd, begin * 512, SEEK_SET );
1173 /* FIXME: check errors */
1174 write( fd, dataptr, nr_sect * 512 );
1175 close( fd );
1177 else
1178 if (!(fake_success))
1179 return 0;
1181 return 1;
1185 /***********************************************************************
1186 * DRIVE_GetFreeSpace
1188 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
1189 PULARGE_INTEGER available )
1191 struct statfs info;
1193 if (!DRIVE_IsValid(drive))
1195 SetLastError( ERROR_INVALID_DRIVE );
1196 return 0;
1199 /* FIXME: add autoconf check for this */
1200 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1201 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
1202 #else
1203 if (statfs( DOSDrives[drive].root, &info) < 0)
1204 #endif
1206 FILE_SetDosError();
1207 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
1208 return 0;
1211 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
1212 #ifdef STATFS_HAS_BAVAIL
1213 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
1214 #else
1215 # ifdef STATFS_HAS_BFREE
1216 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
1217 # else
1218 # error "statfs has no bfree/bavail member!"
1219 # endif
1220 #endif
1221 if (DOSDrives[drive].type == DRIVE_CDROM)
1222 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1223 available->QuadPart = 0;
1225 return 1;
1228 /***********************************************************************
1229 * DRIVE_GetCurrentDirectory
1230 * Returns "X:\\path\\etc\\".
1232 * Despite the API description, return required length including the
1233 * terminating null when buffer too small. This is the real behaviour.
1236 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
1238 UINT ret;
1239 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1241 assert(s);
1242 ret = strlen(s) + 3; /* length of WHOLE current directory */
1243 if (ret >= buflen) return ret + 1;
1244 lstrcpynA( buf, "A:\\", min( 4u, buflen ) );
1245 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
1246 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
1247 return ret;
1251 /***********************************************************************
1252 * DRIVE_BuildEnv
1254 * Build the environment array containing the drives' current directories.
1255 * Resulting pointer must be freed with HeapFree.
1257 char *DRIVE_BuildEnv(void)
1259 int i, length = 0;
1260 const char *cwd[MAX_DOS_DRIVES];
1261 char *env, *p;
1263 for (i = 0; i < MAX_DOS_DRIVES; i++)
1265 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0]) length += strlen(cwd[i]) + 8;
1267 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
1268 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
1270 if (cwd[i] && cwd[i][0])
1271 p += sprintf( p, "=%c:=%c:\\%s", 'A'+i, 'A'+i, cwd[i] ) + 1;
1273 *p = 0;
1274 return env;
1278 /***********************************************************************
1279 * GetDiskFreeSpace (KERNEL.422)
1281 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
1282 LPDWORD sector_bytes, LPDWORD free_clusters,
1283 LPDWORD total_clusters )
1285 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
1286 free_clusters, total_clusters );
1290 /***********************************************************************
1291 * GetDiskFreeSpaceA (KERNEL32.@)
1293 * Fails if expression resulting from current drive's dir and "root"
1294 * is not a root dir of the target drive.
1296 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1297 * if the corresponding info is unneeded.
1299 * FIXME: needs to support UNC names from Win95 OSR2 on.
1301 * Behaviour under Win95a:
1302 * CurrDir root result
1303 * "E:\\TEST" "E:" FALSE
1304 * "E:\\" "E:" TRUE
1305 * "E:\\" "E" FALSE
1306 * "E:\\" "\\" TRUE
1307 * "E:\\TEST" "\\" TRUE
1308 * "E:\\TEST" ":\\" FALSE
1309 * "E:\\TEST" "E:\\" TRUE
1310 * "E:\\TEST" "" FALSE
1311 * "E:\\" "" FALSE (!)
1312 * "E:\\" 0x0 TRUE
1313 * "E:\\TEST" 0x0 TRUE (!)
1314 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1315 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1317 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1318 LPDWORD sector_bytes, LPDWORD free_clusters,
1319 LPDWORD total_clusters )
1321 int drive, sec_size;
1322 ULARGE_INTEGER size,available;
1323 LPCSTR path;
1324 DWORD cluster_sec;
1326 if ((!root) || (strcmp(root,"\\") == 0))
1327 drive = DRIVE_GetCurrentDrive();
1328 else
1329 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
1331 drive = toupper(root[0]) - 'A';
1332 path = &root[2];
1333 if (path[0] == '\0')
1334 path = DRIVE_GetDosCwd(drive);
1335 else
1336 if (path[0] == '\\')
1337 path++;
1338 if (path[0]) /* oops, we are in a subdir */
1340 SetLastError(ERROR_INVALID_NAME);
1341 return FALSE;
1344 else
1346 SetLastError(ERROR_INVALID_NAME);
1347 return FALSE;
1350 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1352 /* Cap the size and available at 2GB as per specs. */
1353 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1355 size.s.HighPart = 0;
1356 size.s.LowPart = 0x7fffffff;
1358 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1360 available.s.HighPart =0;
1361 available.s.LowPart = 0x7fffffff;
1363 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1364 size.s.LowPart /= sec_size;
1365 available.s.LowPart /= sec_size;
1366 /* FIXME: probably have to adjust those variables too for CDFS */
1367 cluster_sec = 1;
1368 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1370 if (cluster_sectors)
1371 *cluster_sectors = cluster_sec;
1372 if (sector_bytes)
1373 *sector_bytes = sec_size;
1374 if (free_clusters)
1375 *free_clusters = available.s.LowPart / cluster_sec;
1376 if (total_clusters)
1377 *total_clusters = size.s.LowPart / cluster_sec;
1378 return TRUE;
1382 /***********************************************************************
1383 * GetDiskFreeSpaceW (KERNEL32.@)
1385 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1386 LPDWORD sector_bytes, LPDWORD free_clusters,
1387 LPDWORD total_clusters )
1389 LPSTR xroot;
1390 BOOL ret;
1392 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1393 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
1394 free_clusters, total_clusters );
1395 HeapFree( GetProcessHeap(), 0, xroot );
1396 return ret;
1400 /***********************************************************************
1401 * GetDiskFreeSpaceExA (KERNEL32.@)
1403 * This function is used to acquire the size of the available and
1404 * total space on a logical volume.
1406 * RETURNS
1408 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1409 * detailed error information.
1412 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1413 PULARGE_INTEGER avail,
1414 PULARGE_INTEGER total,
1415 PULARGE_INTEGER totalfree)
1417 int drive;
1418 ULARGE_INTEGER size,available;
1420 if (!root) drive = DRIVE_GetCurrentDrive();
1421 else
1422 { /* C: always works for GetDiskFreeSpaceEx */
1423 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
1425 FIXME("there are valid root names which are not supported yet\n");
1426 /* ..like UNC names, for instance. */
1428 WARN("invalid root '%s'\n", root );
1429 return FALSE;
1431 drive = toupper(root[0]) - 'A';
1434 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1436 if (total)
1438 total->s.HighPart = size.s.HighPart;
1439 total->s.LowPart = size.s.LowPart;
1442 if (totalfree)
1444 totalfree->s.HighPart = available.s.HighPart;
1445 totalfree->s.LowPart = available.s.LowPart;
1448 if (avail)
1450 if (FIXME_ON(dosfs))
1452 /* On Windows2000, we need to check the disk quota
1453 allocated for the user owning the calling process. We
1454 don't want to be more obtrusive than necessary with the
1455 FIXME messages, so don't print the FIXME unless Wine is
1456 actually masquerading as Windows2000. */
1458 OSVERSIONINFOA ovi;
1459 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1460 if (GetVersionExA(&ovi))
1462 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1463 FIXME("no per-user quota support yet\n");
1467 /* Quick hack, should eventually be fixed to work 100% with
1468 Windows2000 (see comment above). */
1469 avail->s.HighPart = available.s.HighPart;
1470 avail->s.LowPart = available.s.LowPart;
1473 return TRUE;
1476 /***********************************************************************
1477 * GetDiskFreeSpaceExW (KERNEL32.@)
1479 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1480 PULARGE_INTEGER total,
1481 PULARGE_INTEGER totalfree)
1483 LPSTR xroot;
1484 BOOL ret;
1486 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1487 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1488 HeapFree( GetProcessHeap(), 0, xroot );
1489 return ret;
1492 /***********************************************************************
1493 * GetDriveType (KERNEL.136)
1494 * This function returns the type of a drive in Win16.
1495 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1496 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1497 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1498 * do any pseudo-clever changes.
1500 * RETURNS
1501 * drivetype DRIVE_xxx
1503 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1505 UINT type = DRIVE_GetType(drive);
1506 TRACE("(%c:)\n", 'A' + drive );
1507 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1508 return type;
1512 /***********************************************************************
1513 * GetDriveTypeA (KERNEL32.@)
1515 * Returns the type of the disk drive specified. If root is NULL the
1516 * root of the current directory is used.
1518 * RETURNS
1520 * Type of drive (from Win32 SDK):
1522 * DRIVE_UNKNOWN unable to find out anything about the drive
1523 * DRIVE_NO_ROOT_DIR nonexistent root dir
1524 * DRIVE_REMOVABLE the disk can be removed from the machine
1525 * DRIVE_FIXED the disk can not be removed from the machine
1526 * DRIVE_REMOTE network disk
1527 * DRIVE_CDROM CDROM drive
1528 * DRIVE_RAMDISK virtual disk in RAM
1530 UINT WINAPI GetDriveTypeA(LPCSTR root) /* [in] String describing drive */
1532 int drive;
1533 TRACE("(%s)\n", debugstr_a(root));
1535 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1536 else
1538 if ((root[1]) && (root[1] != ':'))
1540 WARN("invalid root %s\n", debugstr_a(root));
1541 return DRIVE_NO_ROOT_DIR;
1543 drive = toupper(root[0]) - 'A';
1545 return DRIVE_GetType(drive);
1549 /***********************************************************************
1550 * GetDriveTypeW (KERNEL32.@)
1552 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1554 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1555 UINT ret = GetDriveTypeA( xpath );
1556 HeapFree( GetProcessHeap(), 0, xpath );
1557 return ret;
1561 /***********************************************************************
1562 * GetCurrentDirectory (KERNEL.411)
1564 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1566 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1570 /***********************************************************************
1571 * GetCurrentDirectoryA (KERNEL32.@)
1573 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1575 UINT ret;
1576 char longname[MAX_PATHNAME_LEN];
1577 char shortname[MAX_PATHNAME_LEN];
1578 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1579 if ( ret > MAX_PATHNAME_LEN ) {
1580 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1581 return ret;
1583 GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1584 ret = strlen( longname ) + 1;
1585 if (ret > buflen) return ret;
1586 strcpy(buf, longname);
1587 return ret - 1;
1590 /***********************************************************************
1591 * GetCurrentDirectoryW (KERNEL32.@)
1593 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1595 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1596 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1597 if (ret < buflen) ret = MultiByteToWideChar( CP_ACP, 0, xpath, -1, buf, buflen ) - 1;
1598 HeapFree( GetProcessHeap(), 0, xpath );
1599 return ret;
1603 /***********************************************************************
1604 * SetCurrentDirectory (KERNEL.412)
1606 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1608 return SetCurrentDirectoryA( dir );
1612 /***********************************************************************
1613 * SetCurrentDirectoryA (KERNEL32.@)
1615 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1617 int drive, olddrive = DRIVE_GetCurrentDrive();
1619 if (!dir) {
1620 ERR_(file)("(NULL)!\n");
1621 return FALSE;
1623 if (dir[0] && (dir[1]==':'))
1625 drive = toupper( *dir ) - 'A';
1626 dir += 2;
1628 else
1629 drive = olddrive;
1631 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1632 sets pTask->curdir only if pTask->curdrive is drive */
1633 if (!(DRIVE_SetCurrentDrive( drive )))
1634 return FALSE;
1635 /* FIXME: what about empty strings? Add a \\ ? */
1636 if (!DRIVE_Chdir( drive, dir )) {
1637 DRIVE_SetCurrentDrive(olddrive);
1638 return FALSE;
1640 return TRUE;
1644 /***********************************************************************
1645 * SetCurrentDirectoryW (KERNEL32.@)
1647 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1649 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1650 BOOL res = SetCurrentDirectoryA( dir );
1651 HeapFree( GetProcessHeap(), 0, dir );
1652 return res;
1656 /***********************************************************************
1657 * GetLogicalDriveStringsA (KERNEL32.@)
1659 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1661 int drive, count;
1663 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1664 if (DRIVE_IsValid(drive)) count++;
1665 if ((count * 4) + 1 <= len)
1667 LPSTR p = buffer;
1668 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1669 if (DRIVE_IsValid(drive))
1671 *p++ = 'a' + drive;
1672 *p++ = ':';
1673 *p++ = '\\';
1674 *p++ = '\0';
1676 *p = '\0';
1677 return count * 4;
1679 else
1680 return (count * 4) + 1; /* account for terminating null */
1681 /* The API tells about these different return values */
1685 /***********************************************************************
1686 * GetLogicalDriveStringsW (KERNEL32.@)
1688 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1690 int drive, count;
1692 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1693 if (DRIVE_IsValid(drive)) count++;
1694 if (count * 4 * sizeof(WCHAR) <= len)
1696 LPWSTR p = buffer;
1697 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1698 if (DRIVE_IsValid(drive))
1700 *p++ = (WCHAR)('a' + drive);
1701 *p++ = (WCHAR)':';
1702 *p++ = (WCHAR)'\\';
1703 *p++ = (WCHAR)'\0';
1705 *p = (WCHAR)'\0';
1707 return count * 4 * sizeof(WCHAR);
1711 /***********************************************************************
1712 * GetLogicalDrives (KERNEL32.@)
1714 DWORD WINAPI GetLogicalDrives(void)
1716 DWORD ret = 0;
1717 int drive;
1719 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1721 if ( (DRIVE_IsValid(drive)) ||
1722 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1723 ret |= (1 << drive);
1725 return ret;
1729 /***********************************************************************
1730 * GetVolumeInformationA (KERNEL32.@)
1732 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1733 DWORD label_len, DWORD *serial,
1734 DWORD *filename_len, DWORD *flags,
1735 LPSTR fsname, DWORD fsname_len )
1737 int drive;
1738 char *cp;
1740 /* FIXME, SetLastError()s missing */
1742 if (!root) drive = DRIVE_GetCurrentDrive();
1743 else
1745 if ((root[1]) && (root[1] != ':'))
1747 WARN("invalid root '%s'\n",root);
1748 return FALSE;
1750 drive = toupper(root[0]) - 'A';
1752 if (!DRIVE_IsValid( drive )) return FALSE;
1753 if (label)
1755 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1756 cp = label + strlen(label);
1757 while (cp != label && *(cp-1) == ' ') cp--;
1758 *cp = '\0';
1760 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1762 /* Set the filesystem information */
1763 /* Note: we only emulate a FAT fs at present */
1765 if (filename_len) {
1766 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1767 *filename_len = 12;
1768 else
1769 *filename_len = 255;
1771 if (flags)
1773 *flags=0;
1774 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1775 *flags|=FS_CASE_SENSITIVE;
1776 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1777 *flags|=FS_CASE_IS_PRESERVED;
1779 if (fsname) {
1780 /* Diablo checks that return code ... */
1781 if (DOSDrives[drive].type == DRIVE_CDROM)
1782 lstrcpynA( fsname, "CDFS", fsname_len );
1783 else
1784 lstrcpynA( fsname, "FAT", fsname_len );
1786 return TRUE;
1790 /***********************************************************************
1791 * GetVolumeInformationW (KERNEL32.@)
1793 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1794 DWORD label_len, DWORD *serial,
1795 DWORD *filename_len, DWORD *flags,
1796 LPWSTR fsname, DWORD fsname_len )
1798 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1799 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1800 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1801 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1802 filename_len, flags, xfsname,
1803 fsname_len );
1804 if (ret)
1806 if (label) MultiByteToWideChar( CP_ACP, 0, xvolname, -1, label, label_len );
1807 if (fsname) MultiByteToWideChar( CP_ACP, 0, xfsname, -1, fsname, fsname_len );
1809 HeapFree( GetProcessHeap(), 0, xroot );
1810 HeapFree( GetProcessHeap(), 0, xvolname );
1811 HeapFree( GetProcessHeap(), 0, xfsname );
1812 return ret;
1815 /***********************************************************************
1816 * SetVolumeLabelA (KERNEL32.@)
1818 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1820 int drive;
1822 /* FIXME, SetLastErrors missing */
1824 if (!root) drive = DRIVE_GetCurrentDrive();
1825 else
1827 if ((root[1]) && (root[1] != ':'))
1829 WARN("invalid root '%s'\n",root);
1830 return FALSE;
1832 drive = toupper(root[0]) - 'A';
1834 if (!DRIVE_IsValid( drive )) return FALSE;
1836 /* some copy protection stuff check this */
1837 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
1839 FIXME("(%s,%s),stub!\n", root, volname);
1840 return TRUE;
1843 /***********************************************************************
1844 * SetVolumeLabelW (KERNEL32.@)
1846 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1848 LPSTR xroot, xvol;
1849 BOOL ret;
1851 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1852 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1853 ret = SetVolumeLabelA( xroot, xvol );
1854 HeapFree( GetProcessHeap(), 0, xroot );
1855 HeapFree( GetProcessHeap(), 0, xvol );
1856 return ret;