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
27 #include "wine/port.h"
34 #include <sys/types.h>
42 #ifdef HAVE_SYS_PARAM_H
43 # include <sys/param.h>
45 #ifdef STATFS_DEFINED_BY_SYS_VFS
48 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
49 # include <sys/mount.h>
51 # ifdef STATFS_DEFINED_BY_SYS_STATFS
52 # include <sys/statfs.h>
59 #include "wine/winbase16.h" /* for GetCurrentTask */
69 #include "wine/unicode.h"
70 #include "wine/library.h"
71 #include "wine/server.h"
72 #include "wine/debug.h"
74 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
75 WINE_DECLARE_DEBUG_CHANNEL(file);
79 char *root; /* root dir in Unix format without trailing / */
80 LPWSTR dos_cwd; /* cwd in DOS format without leading or trailing \ */
81 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
82 char *device; /* raw device path */
83 WCHAR label_conf[12]; /* drive label as cfg'd in wine config */
84 WCHAR label_read[12]; /* drive label as read from device */
85 DWORD serial_conf; /* drive serial number as cfg'd in wine config */
86 UINT type; /* drive type */
87 UINT flags; /* drive flags */
88 UINT codepage; /* drive code page */
89 dev_t dev; /* unix device number */
90 ino_t ino; /* unix inode number */
94 static const WCHAR DRIVE_Types[][8] =
96 { 0 }, /* DRIVE_UNKNOWN */
97 { 0 }, /* DRIVE_NO_ROOT_DIR */
98 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
99 {'h','d',0}, /* DRIVE_FIXED */
100 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
101 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
102 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
106 /* Known filesystem types */
114 static const FS_DESCR DRIVE_Filesystems[] =
116 { {'u','n','i','x',0}, DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
117 { {'m','s','d','o','s',0}, DRIVE_SHORT_NAMES },
118 { {'d','o','s',0}, DRIVE_SHORT_NAMES },
119 { {'f','a','t',0}, DRIVE_SHORT_NAMES },
120 { {'v','f','a','t',0}, DRIVE_CASE_PRESERVING },
121 { {'w','i','n','9','5',0}, DRIVE_CASE_PRESERVING },
126 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
127 static int DRIVE_CurDrive = -1;
129 static HTASK16 DRIVE_LastTask = 0;
131 /* strdup on the process heap */
132 inline static char *heap_strdup( const char *str )
134 INT len = strlen(str) + 1;
135 LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
136 if (p) memcpy( p, str, len );
140 extern void CDROM_InitRegistry(int dev);
142 /***********************************************************************
145 static UINT DRIVE_GetDriveType( LPCWSTR name )
149 static const WCHAR TypeW[] = {'T','y','p','e',0};
150 static const WCHAR hdW[] = {'h','d',0};
152 PROFILE_GetWineIniString( name, TypeW, hdW, buffer, 20 );
155 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
157 if (!strcmpiW( buffer, DRIVE_Types[i] )) return i;
159 MESSAGE("%s: unknown drive type %s, defaulting to 'hd'.\n",
160 debugstr_w(name), debugstr_w(buffer) );
165 /***********************************************************************
168 static UINT DRIVE_GetFSFlags( LPCWSTR name, LPCWSTR value )
170 const FS_DESCR *descr;
172 for (descr = DRIVE_Filesystems; *descr->name; descr++)
173 if (!strcmpiW( value, descr->name )) return descr->flags;
174 MESSAGE("%s: unknown filesystem type %s, defaulting to 'win95'.\n",
175 debugstr_w(name), debugstr_w(value) );
176 return DRIVE_CASE_PRESERVING;
180 /***********************************************************************
185 int i, len, count = 0;
186 WCHAR name[] = {'D','r','i','v','e',' ','A',0};
187 WCHAR drive_env[] = {'=','A',':',0};
188 WCHAR path[MAX_PATHNAME_LEN];
190 struct stat drive_stat_buffer;
193 static const WCHAR PathW[] = {'P','a','t','h',0};
194 static const WCHAR empty_strW[] = { 0 };
195 static const WCHAR CodepageW[] = {'C','o','d','e','p','a','g','e',0};
196 static const WCHAR LabelW[] = {'L','a','b','e','l',0};
197 static const WCHAR SerialW[] = {'S','e','r','i','a','l',0};
198 static const WCHAR zeroW[] = {'0',0};
199 static const WCHAR def_serialW[] = {'1','2','3','4','5','6','7','8',0};
200 static const WCHAR FilesystemW[] = {'F','i','l','e','s','y','s','t','e','m',0};
201 static const WCHAR win95W[] = {'w','i','n','9','5',0};
202 static const WCHAR DeviceW[] = {'D','e','v','i','c','e',0};
203 static const WCHAR ReadVolInfoW[] = {'R','e','a','d','V','o','l','I','n','f','o',0};
204 static const WCHAR FailReadOnlyW[] = {'F','a','i','l','R','e','a','d','O','n','l','y',0};
205 static const WCHAR driveC_labelW[] = {'D','r','i','v','e',' ','C',' ',' ',' ',' ',0};
207 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
209 PROFILE_GetWineIniString( name, PathW, empty_strW, path, MAX_PATHNAME_LEN );
212 /* Get the code page number */
213 PROFILE_GetWineIniString( name, CodepageW, zeroW, /* 0 == CP_ACP */
215 drive->codepage = strtolW( buffer, NULL, 10 );
217 p = path + strlenW(path) - 1;
218 while ((p > path) && (*p == '/')) *p-- = '\0';
222 len = WideCharToMultiByte(drive->codepage, 0, path, -1, NULL, 0, NULL, NULL);
223 drive->root = HeapAlloc(GetProcessHeap(), 0, len);
224 WideCharToMultiByte(drive->codepage, 0, path, -1, drive->root, len, NULL, NULL);
228 /* relative paths are relative to config dir */
229 const char *config = wine_get_config_dir();
230 len = strlen(config);
231 len += WideCharToMultiByte(drive->codepage, 0, path, -1, NULL, 0, NULL, NULL) + 2;
232 drive->root = HeapAlloc( GetProcessHeap(), 0, len );
233 len -= sprintf( drive->root, "%s/", config );
234 WideCharToMultiByte(drive->codepage, 0, path, -1, drive->root + strlen(drive->root), len, NULL, NULL);
237 if (stat( drive->root, &drive_stat_buffer ))
239 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
240 drive->root, strerror(errno), 'A' + i);
241 HeapFree( GetProcessHeap(), 0, drive->root );
245 if (!S_ISDIR(drive_stat_buffer.st_mode))
247 MESSAGE("%s is not a directory, ignoring drive %c:\n",
248 drive->root, 'A' + i );
249 HeapFree( GetProcessHeap(), 0, drive->root );
254 drive->dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(drive->dos_cwd[0]));
255 drive->unix_cwd = heap_strdup( "" );
256 drive->type = DRIVE_GetDriveType( name );
257 drive->device = NULL;
259 drive->dev = drive_stat_buffer.st_dev;
260 drive->ino = drive_stat_buffer.st_ino;
262 /* Get the drive label */
263 PROFILE_GetWineIniString( name, LabelW, empty_strW, drive->label_conf, 12 );
264 if ((len = strlenW(drive->label_conf)) < 11)
266 /* Pad label with spaces */
267 while(len < 11) drive->label_conf[len++] = ' ';
268 drive->label_conf[11] = '\0';
271 /* Get the serial number */
272 PROFILE_GetWineIniString( name, SerialW, def_serialW, buffer, 80 );
273 drive->serial_conf = strtolW( buffer, NULL, 16 );
275 /* Get the filesystem type */
276 PROFILE_GetWineIniString( name, FilesystemW, win95W, buffer, 80 );
277 drive->flags = DRIVE_GetFSFlags( name, buffer );
280 PROFILE_GetWineIniString( name, DeviceW, empty_strW, buffer, 80 );
284 len = WideCharToMultiByte(CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL);
285 drive->device = HeapAlloc(GetProcessHeap(), 0, len);
286 WideCharToMultiByte(drive->codepage, 0, buffer, -1, drive->device, len, NULL, NULL);
288 if (PROFILE_GetWineIniBool( name, ReadVolInfoW, 1))
289 drive->flags |= DRIVE_READ_VOL_INFO;
291 if (drive->type == DRIVE_CDROM)
293 if ((cd_fd = open(drive->device, O_RDONLY|O_NONBLOCK)) != -1)
295 CDROM_InitRegistry(cd_fd);
301 /* Get the FailReadOnly flag */
302 if (PROFILE_GetWineIniBool( name, FailReadOnlyW, 0 ))
303 drive->flags |= DRIVE_FAIL_READ_ONLY;
305 /* Make the first hard disk the current drive */
306 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
310 TRACE("%s: path=%s type=%s label=%s serial=%08lx "
311 "flags=%08x codepage=%u dev=%x ino=%x\n",
312 debugstr_w(name), drive->root, debugstr_w(DRIVE_Types[drive->type]),
313 debugstr_w(drive->label_conf), drive->serial_conf, drive->flags,
314 drive->codepage, (int)drive->dev, (int)drive->ino );
316 else WARN("%s: not defined\n", debugstr_w(name) );
321 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
322 /* Create a C drive pointing to Unix root dir */
323 DOSDrives[2].root = heap_strdup( "/" );
324 DOSDrives[2].dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DOSDrives[2].dos_cwd[0]));
325 DOSDrives[2].unix_cwd = heap_strdup( "" );
326 strcpyW( DOSDrives[2].label_conf, driveC_labelW );
327 DOSDrives[2].serial_conf = 12345678;
328 DOSDrives[2].type = DRIVE_FIXED;
329 DOSDrives[2].device = NULL;
330 DOSDrives[2].flags = 0;
334 /* Make sure the current drive is valid */
335 if (DRIVE_CurDrive == -1)
337 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
339 if (drive->root && !(drive->flags & DRIVE_DISABLED))
347 /* get current working directory info for all drives */
348 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
350 if (!GetEnvironmentVariableW(drive_env, path, MAX_PATHNAME_LEN)) continue;
352 if (toupperW(path[0]) != drive_env[1] || path[1] != ':') continue;
353 DRIVE_Chdir( i, path + 2 );
359 /***********************************************************************
362 int DRIVE_IsValid( int drive )
364 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
365 return (DOSDrives[drive].root &&
366 !(DOSDrives[drive].flags & DRIVE_DISABLED));
370 /***********************************************************************
371 * DRIVE_GetCurrentDrive
373 int DRIVE_GetCurrentDrive(void)
375 TDB *pTask = TASK_GetCurrent();
376 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
377 return DRIVE_CurDrive;
381 /***********************************************************************
382 * DRIVE_SetCurrentDrive
384 int DRIVE_SetCurrentDrive( int drive )
386 TDB *pTask = TASK_GetCurrent();
387 if (!DRIVE_IsValid( drive ))
389 SetLastError( ERROR_INVALID_DRIVE );
392 TRACE("%c:\n", 'A' + drive );
393 DRIVE_CurDrive = drive;
394 if (pTask) pTask->curdrive = drive | 0x80;
399 /***********************************************************************
400 * DRIVE_FindDriveRoot
402 * Find a drive for which the root matches the beginning of the given path.
403 * This can be used to translate a Unix path into a drive + DOS path.
404 * Return value is the drive, or -1 on error. On success, path is modified
405 * to point to the beginning of the DOS path.
407 * Note: path must be in the encoding of the underlying Unix file system.
409 int DRIVE_FindDriveRoot( const char **path )
411 /* Starting with the full path, check if the device and inode match any of
412 * the wine 'drives'. If not then remove the last path component and try
413 * again. If the last component was a '..' then skip a normal component
414 * since it's a directory that's ascended back out of.
416 int drive, level, len;
417 char buffer[MAX_PATHNAME_LEN];
421 strcpy( buffer, *path );
422 while ((p = strchr( buffer, '\\' )) != NULL)
424 len = strlen(buffer);
426 /* strip off trailing slashes */
427 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
432 if (stat( buffer, &st ) == 0 && S_ISDIR( st.st_mode ))
434 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
436 if (!DOSDrives[drive].root ||
437 (DOSDrives[drive].flags & DRIVE_DISABLED))
440 if ((DOSDrives[drive].dev == st.st_dev) &&
441 (DOSDrives[drive].ino == st.st_ino))
443 if (len == 1) len = 0; /* preserve root slash in returned path */
444 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
445 *path, 'A' + drive, buffer, *path + len);
447 if (!**path) *path = "\\";
452 if (len <= 1) return -1; /* reached root */
457 /* find start of the last path component */
458 while (len > 1 && buffer[len - 1] != '/') len--;
459 if (!buffer[len]) break; /* empty component -> reached root */
460 /* does removing it take us up a level? */
461 if (strcmp( buffer + len, "." ) != 0)
462 level += strcmp( buffer + len, ".." ) ? 1 : -1;
464 /* strip off trailing slashes */
465 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
471 /***********************************************************************
472 * DRIVE_FindDriveRootW
474 * Unicode version of DRIVE_FindDriveRoot.
476 int DRIVE_FindDriveRootW( LPCWSTR *path )
478 int drive, rootdrive = -1;
479 char buffer[MAX_PATHNAME_LEN];
481 int len, match_len = -1;
483 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
485 if (!DOSDrives[drive].root ||
486 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
488 WideCharToMultiByte(DOSDrives[drive].codepage, 0, *path, -1,
489 buffer, MAX_PATHNAME_LEN, NULL, NULL);
491 len = strlen(DOSDrives[drive].root);
492 if(strncmp(DOSDrives[drive].root, buffer, len))
494 if(len <= match_len) continue;
503 TRACE("%s -> drive %c:, root='%s', name=%s\n",
504 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, debugstr_w(*path) );
510 /***********************************************************************
513 const char * DRIVE_GetRoot( int drive )
515 if (!DRIVE_IsValid( drive )) return NULL;
516 return DOSDrives[drive].root;
520 /***********************************************************************
523 LPCWSTR DRIVE_GetDosCwd( int drive )
525 TDB *pTask = TASK_GetCurrent();
526 if (!DRIVE_IsValid( drive )) return NULL;
528 /* Check if we need to change the directory to the new task. */
529 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
530 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
531 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
533 static const WCHAR rootW[] = {'\\',0};
534 WCHAR curdirW[MAX_PATH];
535 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
536 /* Perform the task-switch */
537 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
538 DRIVE_LastTask = GetCurrentTask();
540 return DOSDrives[drive].dos_cwd;
544 /***********************************************************************
547 const char * DRIVE_GetUnixCwd( int drive )
549 TDB *pTask = TASK_GetCurrent();
550 if (!DRIVE_IsValid( drive )) return NULL;
552 /* Check if we need to change the directory to the new task. */
553 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
554 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
555 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
557 static const WCHAR rootW[] = {'\\',0};
558 WCHAR curdirW[MAX_PATH];
559 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
560 /* Perform the task-switch */
561 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
562 DRIVE_LastTask = GetCurrentTask();
564 return DOSDrives[drive].unix_cwd;
568 /***********************************************************************
571 const char * DRIVE_GetDevice( int drive )
573 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
576 /******************************************************************
577 * static WORD CDROM_Data_FindBestVoldesc
581 static WORD CDROM_Data_FindBestVoldesc(int fd)
583 BYTE cur_vd_type, max_vd_type = 0;
584 unsigned int offs, best_offs = 0, extra_offs = 0;
587 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
589 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
590 * the volume label is displaced forward by 8
592 lseek(fd, offs + 11, SEEK_SET); /* check for non-ISO9660 signature */
594 if ((sig[0] == 'R') && (sig[1] == 'O') && (sig[2]=='M'))
598 lseek(fd, offs + extra_offs, SEEK_SET);
599 read(fd, &cur_vd_type, 1);
600 if (cur_vd_type == 0xff) /* voldesc set terminator */
602 if (cur_vd_type > max_vd_type)
604 max_vd_type = cur_vd_type;
605 best_offs = offs + extra_offs;
611 /***********************************************************************
612 * DRIVE_ReadSuperblock
615 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
616 * to check, that they are writing on a FAT filesystem !
618 int DRIVE_ReadSuperblock (int drive, char * buff)
620 #define DRIVE_SUPER 96
625 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
626 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
629 if (!DOSDrives[drive].device)
630 ERR("No device configured for drive %c: !\n", 'A'+drive);
632 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
633 (stat(DOSDrives[drive].device, &st)) ?
634 "not available or symlink not valid ?" : "no permission");
635 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
636 PROFILE_UsageWineIni();
640 switch(DOSDrives[drive].type)
642 case DRIVE_REMOVABLE:
647 offs = CDROM_Data_FindBestVoldesc(fd);
654 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs))
659 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER)
665 switch(DOSDrives[drive].type)
667 case DRIVE_REMOVABLE:
669 if ((buff[0x26]!=0x29) || /* Check for FAT present */
670 /* FIXME: do really all FAT have their name beginning with
671 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
672 memcmp( buff+0x36,"FAT",3))
674 ERR("The filesystem is not FAT !! (device=%s)\n",
675 DOSDrives[drive].device);
681 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
686 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
700 /***********************************************************************
701 * DRIVE_WriteSuperblockEntry
704 * We are writing as little as possible (ie. not the whole SuperBlock)
705 * not to interfere with kernel. The drive can be mounted !
707 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
711 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
713 ERR("Cannot open the device %s (for writing)\n",
714 DOSDrives[drive].device);
717 if (lseek(fd,ofs,SEEK_SET)!=ofs)
719 ERR("lseek failed on device %s !\n",
720 DOSDrives[drive].device);
724 if (write(fd,buff,len)!=len)
727 ERR("Cannot write on %s !\n",
728 DOSDrives[drive].device);
734 /******************************************************************
735 * static HANDLE CDROM_Open
739 static HANDLE CDROM_Open(int drive)
741 WCHAR root[] = {'\\','\\','.','\\','A',':',0};
743 return CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
746 /**************************************************************************
747 * CDROM_Data_GetLabel [internal]
749 DWORD CDROM_Data_GetLabel(int drive, WCHAR *label)
751 #define LABEL_LEN 32+1
752 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
753 WORD offs = CDROM_Data_FindBestVoldesc(dev);
754 WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
755 DWORD unicode_id = 0;
759 if ((lseek(dev, offs+0x58, SEEK_SET) == offs+0x58)
760 && (read(dev, &unicode_id, 3) == 3))
762 int ver = (unicode_id & 0xff0000) >> 16;
764 if ((lseek(dev, offs+0x28, SEEK_SET) != offs+0x28)
765 || (read(dev, &label_read, LABEL_LEN) != LABEL_LEN))
769 if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
770 && ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
771 { /* yippee, unicode */
774 for (i=0; i<LABEL_LEN;i++)
775 { /* Motorola -> Intel Unicode conversion :-\ */
777 label_read[i] = (ch << 8) | (ch >> 8);
779 strncpyW(label, label_read, 11);
784 MultiByteToWideChar(DOSDrives[drive].codepage, 0, (LPSTR)label_read, -1, label, 11);
792 ERR("error reading label !\n");
796 /**************************************************************************
797 * CDROM_GetLabel [internal]
799 static DWORD CDROM_GetLabel(int drive, WCHAR *label)
801 HANDLE h = CDROM_Open(drive);
806 if (!h || !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
809 switch (cdd.DiskData & 0x03)
811 case CDROM_DISK_DATA_TRACK:
812 if (!CDROM_Data_GetLabel(drive, label))
815 case CDROM_DISK_AUDIO_TRACK:
817 static const WCHAR audioCD[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
818 strcpyW(label, audioCD);
821 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
822 FIXME("Need to get the label of a mixed mode CD: not implemented yet !\n");
828 TRACE("CD: label is %s\n", debugstr_w(label));
832 /***********************************************************************
835 LPCWSTR DRIVE_GetLabel( int drive )
838 char buff[DRIVE_SUPER];
841 if (!DRIVE_IsValid( drive )) return NULL;
842 if (DOSDrives[drive].type == DRIVE_CDROM)
844 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
847 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
849 if (DRIVE_ReadSuperblock(drive,(char *) buff))
850 ERR("Invalid or unreadable superblock on %s (%c:).\n",
851 DOSDrives[drive].device, (char)(drive+'A'));
853 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
854 DOSDrives[drive].type == DRIVE_FIXED)
857 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
859 MultiByteToWideChar(DOSDrives[drive].codepage, 0, buff+offs, 11,
860 DOSDrives[drive].label_read, 11);
861 DOSDrives[drive].label_read[11]='\0';
867 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
870 #define CDFRAMES_PERSEC 75
871 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
872 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
873 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
875 /**************************************************************************
876 * CDROM_Audio_GetSerial [internal]
878 static DWORD CDROM_Audio_GetSerial(HANDLE h)
880 unsigned long serial = 0;
883 DWORD dwStart, dwEnd, br;
886 if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
890 * wMagic collects the wFrames from track 1
891 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
893 * There it is collected for correcting the serial when there are less than
896 wMagic = toc.TrackData[0].Address[2];
897 dwStart = FRAME_OF_TOC(toc, toc.FirstTrack);
899 for (i = 0; i <= toc.LastTrack - toc.FirstTrack; i++) {
900 serial += (toc.TrackData[i].Address[0] << 16) |
901 (toc.TrackData[i].Address[1] << 8) | toc.TrackData[i].Address[2];
903 dwEnd = FRAME_OF_TOC(toc, toc.LastTrack + 1);
905 if (toc.LastTrack - toc.FirstTrack + 1 < 3)
906 serial += wMagic + (dwEnd - dwStart);
911 /**************************************************************************
912 * CDROM_Data_GetSerial [internal]
914 static DWORD CDROM_Data_GetSerial(int drive)
916 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
922 BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
925 if (dev == -1) return 0;
926 offs = CDROM_Data_FindBestVoldesc(dev);
935 lseek(dev, offs, SEEK_SET);
936 read(dev, buf, 2048);
938 * OK, another braindead one... argh. Just believe it.
939 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
940 * It's true and nobody will ever be able to change it.
942 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
944 if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (ovi.dwMajorVersion >= 4))
946 b0 = 3; b1 = 2; b2 = 1; b3 = 0;
948 for (i = 0; i < 2048; i += 4)
950 /* DON'T optimize this into DWORD !! (breaks overflow) */
951 serial.p[b0] += buf[i+b0];
952 serial.p[b1] += buf[i+b1];
953 serial.p[b2] += buf[i+b2];
954 serial.p[b3] += buf[i+b3];
961 /**************************************************************************
962 * CDROM_GetSerial [internal]
964 static DWORD CDROM_GetSerial(int drive)
967 HANDLE h = CDROM_Open(drive);
971 if (!h || ! !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
974 switch (cdd.DiskData & 0x03)
976 case CDROM_DISK_DATA_TRACK:
977 /* hopefully a data CD */
978 serial = CDROM_Data_GetSerial(drive);
980 case CDROM_DISK_AUDIO_TRACK:
982 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
983 serial = CDROM_Audio_GetSerial(h);
990 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
997 /***********************************************************************
998 * DRIVE_GetSerialNumber
1000 DWORD DRIVE_GetSerialNumber( int drive )
1003 char buff[DRIVE_SUPER];
1005 TRACE("drive %d, type = %d\n", drive, DOSDrives[drive].type);
1007 if (!DRIVE_IsValid( drive )) return 0;
1009 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1011 switch(DOSDrives[drive].type)
1013 case DRIVE_REMOVABLE:
1015 if (DRIVE_ReadSuperblock(drive,(char *) buff))
1016 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
1017 " Maybe not FAT?\n" ,
1018 DOSDrives[drive].device, 'A'+drive);
1020 serial = *((DWORD*)(buff+0x27));
1023 serial = CDROM_GetSerial(drive);
1026 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
1030 return (serial) ? serial : DOSDrives[drive].serial_conf;
1034 /***********************************************************************
1035 * DRIVE_SetSerialNumber
1037 int DRIVE_SetSerialNumber( int drive, DWORD serial )
1039 char buff[DRIVE_SUPER];
1041 if (!DRIVE_IsValid( drive )) return 0;
1043 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1045 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
1046 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
1047 /* check, if the drive has a FAT filesystem */
1048 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
1049 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
1053 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
1054 DOSDrives[drive].serial_conf = serial;
1059 /***********************************************************************
1062 static UINT DRIVE_GetType( int drive )
1064 if (!DRIVE_IsValid( drive )) return DRIVE_NO_ROOT_DIR;
1065 return DOSDrives[drive].type;
1069 /***********************************************************************
1072 UINT DRIVE_GetFlags( int drive )
1074 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1075 return DOSDrives[drive].flags;
1078 /***********************************************************************
1081 UINT DRIVE_GetCodepage( int drive )
1083 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1084 return DOSDrives[drive].codepage;
1088 /***********************************************************************
1091 int DRIVE_Chdir( int drive, LPCWSTR path )
1093 DOS_FULL_NAME full_name;
1094 WCHAR buffer[MAX_PATHNAME_LEN];
1096 BY_HANDLE_FILE_INFORMATION info;
1097 TDB *pTask = TASK_GetCurrent();
1099 buffer[0] = 'A' + drive;
1102 TRACE("(%s,%s)\n", debugstr_w(buffer), debugstr_w(path) );
1103 strncpyW( buffer + 2, path, MAX_PATHNAME_LEN - 2 );
1104 buffer[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1106 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
1107 if (!FILE_Stat( full_name.long_name, &info, NULL )) return 0;
1108 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1110 SetLastError( ERROR_FILE_NOT_FOUND );
1113 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
1114 while (*unix_cwd == '/') unix_cwd++;
1116 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1117 'A' + drive, unix_cwd, debugstr_w(full_name.short_name + 3) );
1119 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
1120 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
1121 DOSDrives[drive].dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name.short_name) - 2) * sizeof(WCHAR));
1122 strcpyW(DOSDrives[drive].dos_cwd, full_name.short_name + 3);
1123 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
1125 if (pTask && (pTask->curdrive & 0x80) &&
1126 ((pTask->curdrive & ~0x80) == drive))
1128 WideCharToMultiByte(CP_ACP, 0, full_name.short_name + 2, -1,
1129 pTask->curdir, sizeof(pTask->curdir), NULL, NULL);
1130 DRIVE_LastTask = GetCurrentTask();
1136 /***********************************************************************
1139 int DRIVE_Disable( int drive )
1141 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1143 SetLastError( ERROR_INVALID_DRIVE );
1146 DOSDrives[drive].flags |= DRIVE_DISABLED;
1151 /***********************************************************************
1154 int DRIVE_Enable( int drive )
1156 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1158 SetLastError( ERROR_INVALID_DRIVE );
1161 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1166 /***********************************************************************
1167 * DRIVE_SetLogicalMapping
1169 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
1171 /* If new_drive is already valid, do nothing and return 0
1172 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1174 DOSDRIVE *old, *new;
1176 old = DOSDrives + existing_drive;
1177 new = DOSDrives + new_drive;
1179 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
1181 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
1183 SetLastError( ERROR_INVALID_DRIVE );
1189 TRACE("Can't map drive %c: to already existing drive %c:\n",
1190 'A' + existing_drive, 'A' + new_drive );
1191 /* it is already mapped there, so return success */
1192 if (!strcmp(old->root,new->root))
1197 new->root = heap_strdup( old->root );
1198 new->dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(old->dos_cwd) + 1) * sizeof(WCHAR));
1199 strcpyW(new->dos_cwd, old->dos_cwd);
1200 new->unix_cwd = heap_strdup( old->unix_cwd );
1201 new->device = heap_strdup( old->device );
1202 memcpy ( new->label_conf, old->label_conf, 12 );
1203 memcpy ( new->label_read, old->label_read, 12 );
1204 new->serial_conf = old->serial_conf;
1205 new->type = old->type;
1206 new->flags = old->flags;
1207 new->dev = old->dev;
1208 new->ino = old->ino;
1210 TRACE("Drive %c: is now equal to drive %c:\n",
1211 'A' + new_drive, 'A' + existing_drive );
1217 /***********************************************************************
1220 * Open the drive raw device and return a Unix fd (or -1 on error).
1222 int DRIVE_OpenDevice( int drive, int flags )
1224 if (!DRIVE_IsValid( drive )) return -1;
1225 return open( DOSDrives[drive].device, flags );
1229 /***********************************************************************
1232 * Read raw sectors from a device
1234 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1238 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1240 lseek( fd, begin * 512, SEEK_SET );
1241 /* FIXME: check errors */
1242 read( fd, dataptr, nr_sect * 512 );
1247 memset(dataptr, 0, nr_sect * 512);
1250 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
1251 if (begin == 1) *dataptr = 0xf8;
1260 /***********************************************************************
1263 * Write raw sectors to a device
1265 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1269 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1271 lseek( fd, begin * 512, SEEK_SET );
1272 /* FIXME: check errors */
1273 write( fd, dataptr, nr_sect * 512 );
1277 if (!(fake_success))
1284 /***********************************************************************
1285 * DRIVE_GetFreeSpace
1287 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
1288 PULARGE_INTEGER available )
1292 if (!DRIVE_IsValid(drive))
1294 SetLastError( ERROR_PATH_NOT_FOUND );
1298 /* FIXME: add autoconf check for this */
1299 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1300 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
1302 if (statfs( DOSDrives[drive].root, &info) < 0)
1306 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
1310 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
1311 #ifdef STATFS_HAS_BAVAIL
1312 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
1314 # ifdef STATFS_HAS_BFREE
1315 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
1317 # error "statfs has no bfree/bavail member!"
1320 if (DOSDrives[drive].type == DRIVE_CDROM)
1321 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1322 available->QuadPart = 0;
1327 /***********************************************************************
1328 * DRIVE_GetCurrentDirectory
1329 * Returns "X:\\path\\etc\\".
1331 * Despite the API description, return required length including the
1332 * terminating null when buffer too small. This is the real behaviour.
1334 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPWSTR buf )
1337 LPCWSTR dos_cwd = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1338 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1340 ret = strlenW(dos_cwd) + 3; /* length of WHOLE current directory */
1341 if (ret >= buflen) return ret + 1;
1343 strcpyW( buf, driveA_rootW );
1344 buf[0] += DRIVE_GetCurrentDrive();
1345 strcatW( buf, dos_cwd );
1350 /***********************************************************************
1353 * Build the environment array containing the drives' current directories.
1354 * Resulting pointer must be freed with HeapFree.
1356 char *DRIVE_BuildEnv(void)
1359 LPCWSTR cwd[MAX_DOS_DRIVES];
1362 for (i = 0; i < MAX_DOS_DRIVES; i++)
1364 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0])
1365 length += WideCharToMultiByte(DRIVE_GetCodepage(i), 0,
1366 cwd[i], -1, NULL, 0, NULL, NULL) + 7;
1368 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
1369 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
1371 if (cwd[i] && cwd[i][0])
1373 *p++ = '='; *p++ = 'A' + i; *p++ = ':';
1374 *p++ = '='; *p++ = 'A' + i; *p++ = ':'; *p++ = '\\';
1375 WideCharToMultiByte(DRIVE_GetCodepage(i), 0, cwd[i], -1, p, 0x7fffffff, NULL, NULL);
1384 /***********************************************************************
1385 * GetDiskFreeSpace (KERNEL.422)
1387 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
1388 LPDWORD sector_bytes, LPDWORD free_clusters,
1389 LPDWORD total_clusters )
1391 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
1392 free_clusters, total_clusters );
1396 /***********************************************************************
1397 * GetDiskFreeSpaceW (KERNEL32.@)
1399 * Fails if expression resulting from current drive's dir and "root"
1400 * is not a root dir of the target drive.
1402 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1403 * if the corresponding info is unneeded.
1405 * FIXME: needs to support UNC names from Win95 OSR2 on.
1407 * Behaviour under Win95a:
1408 * CurrDir root result
1409 * "E:\\TEST" "E:" FALSE
1413 * "E:\\TEST" "\\" TRUE
1414 * "E:\\TEST" ":\\" FALSE
1415 * "E:\\TEST" "E:\\" TRUE
1416 * "E:\\TEST" "" FALSE
1417 * "E:\\" "" FALSE (!)
1419 * "E:\\TEST" 0x0 TRUE (!)
1420 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1421 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1423 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1424 LPDWORD sector_bytes, LPDWORD free_clusters,
1425 LPDWORD total_clusters )
1427 int drive, sec_size;
1428 ULARGE_INTEGER size,available;
1432 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root), cluster_sectors, sector_bytes,
1433 free_clusters, total_clusters);
1435 if (!root || root[0] == '\\' || root[0] == '/')
1436 drive = DRIVE_GetCurrentDrive();
1438 if (root[0] && root[1] == ':') /* root contains drive tag */
1440 drive = toupperW(root[0]) - 'A';
1442 if (path[0] == '\0')
1444 path = DRIVE_GetDosCwd(drive);
1447 SetLastError(ERROR_PATH_NOT_FOUND);
1452 if (path[0] == '\\')
1455 if (path[0]) /* oops, we are in a subdir */
1457 SetLastError(ERROR_INVALID_NAME);
1464 SetLastError(ERROR_PATH_NOT_FOUND);
1466 SetLastError(ERROR_INVALID_NAME);
1470 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1472 /* Cap the size and available at 2GB as per specs. */
1473 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1475 size.s.HighPart = 0;
1476 size.s.LowPart = 0x7fffffff;
1478 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1480 available.s.HighPart =0;
1481 available.s.LowPart = 0x7fffffff;
1483 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1484 size.s.LowPart /= sec_size;
1485 available.s.LowPart /= sec_size;
1486 /* FIXME: probably have to adjust those variables too for CDFS */
1488 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1490 if (cluster_sectors)
1491 *cluster_sectors = cluster_sec;
1493 *sector_bytes = sec_size;
1495 *free_clusters = available.s.LowPart / cluster_sec;
1497 *total_clusters = size.s.LowPart / cluster_sec;
1502 /***********************************************************************
1503 * GetDiskFreeSpaceA (KERNEL32.@)
1505 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1506 LPDWORD sector_bytes, LPDWORD free_clusters,
1507 LPDWORD total_clusters )
1509 UNICODE_STRING rootW;
1514 if(!RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1516 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1521 rootW.Buffer = NULL;
1523 ret = GetDiskFreeSpaceW(rootW.Buffer, cluster_sectors, sector_bytes,
1524 free_clusters, total_clusters );
1525 RtlFreeUnicodeString(&rootW);
1531 /***********************************************************************
1532 * GetDiskFreeSpaceExA (KERNEL32.@)
1534 * This function is used to acquire the size of the available and
1535 * total space on a logical volume.
1539 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1540 * detailed error information.
1543 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1544 PULARGE_INTEGER avail,
1545 PULARGE_INTEGER total,
1546 PULARGE_INTEGER totalfree)
1549 ULARGE_INTEGER size,available;
1551 if (!root) drive = DRIVE_GetCurrentDrive();
1553 { /* C: always works for GetDiskFreeSpaceEx */
1554 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
1556 FIXME("there are valid root names which are not supported yet\n");
1557 /* ..like UNC names, for instance. */
1559 WARN("invalid root '%s'\n", root );
1562 drive = toupper(root[0]) - 'A';
1565 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1569 total->s.HighPart = size.s.HighPart;
1570 total->s.LowPart = size.s.LowPart;
1575 totalfree->s.HighPart = available.s.HighPart;
1576 totalfree->s.LowPart = available.s.LowPart;
1581 if (FIXME_ON(dosfs))
1583 /* On Windows2000, we need to check the disk quota
1584 allocated for the user owning the calling process. We
1585 don't want to be more obtrusive than necessary with the
1586 FIXME messages, so don't print the FIXME unless Wine is
1587 actually masquerading as Windows2000. */
1590 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1591 if (GetVersionExA(&ovi))
1593 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1594 FIXME("no per-user quota support yet\n");
1598 /* Quick hack, should eventually be fixed to work 100% with
1599 Windows2000 (see comment above). */
1600 avail->s.HighPart = available.s.HighPart;
1601 avail->s.LowPart = available.s.LowPart;
1607 /***********************************************************************
1608 * GetDiskFreeSpaceExW (KERNEL32.@)
1610 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1611 PULARGE_INTEGER total,
1612 PULARGE_INTEGER totalfree)
1617 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1618 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1619 HeapFree( GetProcessHeap(), 0, xroot );
1623 /***********************************************************************
1624 * GetDriveType (KERNEL.136)
1625 * This function returns the type of a drive in Win16.
1626 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1627 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1628 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1629 * do any pseudo-clever changes.
1632 * drivetype DRIVE_xxx
1634 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1636 UINT type = DRIVE_GetType(drive);
1637 TRACE("(%c:)\n", 'A' + drive );
1638 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1643 /***********************************************************************
1644 * GetDriveTypeW (KERNEL32.@)
1646 * Returns the type of the disk drive specified. If root is NULL the
1647 * root of the current directory is used.
1651 * Type of drive (from Win32 SDK):
1653 * DRIVE_UNKNOWN unable to find out anything about the drive
1654 * DRIVE_NO_ROOT_DIR nonexistent root dir
1655 * DRIVE_REMOVABLE the disk can be removed from the machine
1656 * DRIVE_FIXED the disk can not be removed from the machine
1657 * DRIVE_REMOTE network disk
1658 * DRIVE_CDROM CDROM drive
1659 * DRIVE_RAMDISK virtual disk in RAM
1661 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1664 TRACE("(%s)\n", debugstr_w(root));
1666 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1669 if ((root[1]) && (root[1] != ':'))
1671 WARN("invalid root %s\n", debugstr_w(root));
1672 return DRIVE_NO_ROOT_DIR;
1674 drive = toupperW(root[0]) - 'A';
1676 return DRIVE_GetType(drive);
1680 /***********************************************************************
1681 * GetDriveTypeA (KERNEL32.@)
1683 UINT WINAPI GetDriveTypeA( LPCSTR root )
1685 UNICODE_STRING rootW;
1690 if( !RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1692 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1697 rootW.Buffer = NULL;
1699 ret = GetDriveTypeW(rootW.Buffer);
1701 RtlFreeUnicodeString(&rootW);
1707 /***********************************************************************
1708 * GetCurrentDirectory (KERNEL.411)
1710 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1712 WCHAR cur_dirW[MAX_PATH];
1714 DRIVE_GetCurrentDirectory(MAX_PATH, cur_dirW);
1715 return (UINT16)WideCharToMultiByte(CP_ACP, 0, cur_dirW, -1, buf, buflen, NULL, NULL);
1719 /***********************************************************************
1720 * GetCurrentDirectoryW (KERNEL32.@)
1722 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1725 WCHAR longname[MAX_PATHNAME_LEN];
1726 WCHAR shortname[MAX_PATHNAME_LEN];
1728 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1729 if ( ret > MAX_PATHNAME_LEN ) {
1730 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1733 GetLongPathNameW(shortname, longname, MAX_PATHNAME_LEN);
1734 ret = strlenW( longname ) + 1;
1735 if (ret > buflen) return ret;
1736 strcpyW(buf, longname);
1740 /***********************************************************************
1741 * GetCurrentDirectoryA (KERNEL32.@)
1743 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1745 WCHAR bufferW[MAX_PATH];
1748 retW = GetCurrentDirectoryW(MAX_PATH, bufferW);
1752 else if (retW > MAX_PATH)
1754 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1759 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1762 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, buflen, NULL, NULL);
1763 ret--; /* length without 0 */
1770 /***********************************************************************
1771 * SetCurrentDirectory (KERNEL.412)
1773 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1775 return SetCurrentDirectoryA( dir );
1779 /***********************************************************************
1780 * SetCurrentDirectoryW (KERNEL32.@)
1782 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dir )
1784 int drive, olddrive = DRIVE_GetCurrentDrive();
1788 SetLastError(ERROR_INVALID_PARAMETER);
1791 if (dir[0] && (dir[1]==':'))
1793 drive = toupperW( *dir ) - 'A';
1799 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1800 sets pTask->curdir only if pTask->curdrive is drive */
1801 if (!(DRIVE_SetCurrentDrive( drive )))
1804 /* FIXME: what about empty strings? Add a \\ ? */
1805 if (!DRIVE_Chdir( drive, dir )) {
1806 DRIVE_SetCurrentDrive(olddrive);
1813 /***********************************************************************
1814 * SetCurrentDirectoryA (KERNEL32.@)
1816 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1818 UNICODE_STRING dirW;
1823 SetLastError(ERROR_INVALID_PARAMETER);
1827 if (RtlCreateUnicodeStringFromAsciiz(&dirW, dir))
1829 ret = SetCurrentDirectoryW(dirW.Buffer);
1830 RtlFreeUnicodeString(&dirW);
1833 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1838 /***********************************************************************
1839 * GetLogicalDriveStringsA (KERNEL32.@)
1841 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1845 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1846 if (DRIVE_IsValid(drive)) count++;
1847 if ((count * 4) + 1 <= len)
1850 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1851 if (DRIVE_IsValid(drive))
1862 return (count * 4) + 1; /* account for terminating null */
1863 /* The API tells about these different return values */
1867 /***********************************************************************
1868 * GetLogicalDriveStringsW (KERNEL32.@)
1870 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1874 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1875 if (DRIVE_IsValid(drive)) count++;
1876 if (count * 4 * sizeof(WCHAR) <= len)
1879 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1880 if (DRIVE_IsValid(drive))
1882 *p++ = (WCHAR)('a' + drive);
1889 return count * 4 * sizeof(WCHAR);
1893 /***********************************************************************
1894 * GetLogicalDrives (KERNEL32.@)
1896 DWORD WINAPI GetLogicalDrives(void)
1901 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1903 if ( (DRIVE_IsValid(drive)) ||
1904 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1905 ret |= (1 << drive);
1911 /***********************************************************************
1912 * GetVolumeInformationW (KERNEL32.@)
1914 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1915 DWORD label_len, DWORD *serial,
1916 DWORD *filename_len, DWORD *flags,
1917 LPWSTR fsname, DWORD fsname_len )
1922 /* FIXME, SetLastError()s missing */
1924 if (!root) drive = DRIVE_GetCurrentDrive();
1927 if (root[0] && root[1] != ':')
1929 WARN("invalid root %s\n", debugstr_w(root));
1932 drive = toupperW(root[0]) - 'A';
1934 if (!DRIVE_IsValid( drive )) return FALSE;
1935 if (label && label_len)
1937 strncpyW( label, DRIVE_GetLabel(drive), label_len );
1938 label[label_len - 1] = 0; /* ensure 0 termination */
1939 cp = label + strlenW(label);
1940 while (cp != label && *(cp-1) == ' ') cp--;
1943 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1945 /* Set the filesystem information */
1946 /* Note: we only emulate a FAT fs at present */
1949 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1952 *filename_len = 255;
1957 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1958 *flags|=FS_CASE_SENSITIVE;
1959 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1960 *flags|=FS_CASE_IS_PRESERVED;
1962 if (fsname && fsname_len)
1964 /* Diablo checks that return code ... */
1965 if (DOSDrives[drive].type == DRIVE_CDROM)
1967 static const WCHAR cdfsW[] = {'C','D','F','S',0};
1968 strncpyW( fsname, cdfsW, fsname_len );
1972 static const WCHAR fatW[] = {'F','A','T',0};
1973 strncpyW( fsname, fatW, fsname_len );
1975 fsname[fsname_len - 1] = 0; /* ensure 0 termination */
1981 /***********************************************************************
1982 * GetVolumeInformationA (KERNEL32.@)
1984 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1985 DWORD label_len, DWORD *serial,
1986 DWORD *filename_len, DWORD *flags,
1987 LPSTR fsname, DWORD fsname_len )
1989 UNICODE_STRING rootW;
1990 LPWSTR labelW, fsnameW;
1993 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
1994 else rootW.Buffer = NULL;
1995 labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
1996 fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
1998 if ((ret = GetVolumeInformationW(rootW.Buffer, labelW, label_len, serial,
1999 filename_len, flags, fsnameW, fsname_len)))
2001 if (label) WideCharToMultiByte(CP_ACP, 0, labelW, -1, label, label_len, NULL, NULL);
2002 if (fsname) WideCharToMultiByte(CP_ACP, 0, fsnameW, -1, fsname, fsname_len, NULL, NULL);
2005 RtlFreeUnicodeString(&rootW);
2006 if (labelW) HeapFree( GetProcessHeap(), 0, labelW );
2007 if (fsnameW) HeapFree( GetProcessHeap(), 0, fsnameW );
2011 /***********************************************************************
2012 * SetVolumeLabelW (KERNEL32.@)
2014 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR volname )
2018 /* FIXME, SetLastErrors missing */
2020 if (!root) drive = DRIVE_GetCurrentDrive();
2023 if ((root[1]) && (root[1] != ':'))
2025 WARN("invalid root %s\n", debugstr_w(root));
2028 drive = toupperW(root[0]) - 'A';
2030 if (!DRIVE_IsValid( drive )) return FALSE;
2032 /* some copy protection stuff check this */
2033 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
2035 strncpyW(DOSDrives[drive].label_conf, volname, 12);
2036 DOSDrives[drive].label_conf[12 - 1] = 0; /* ensure 0 termination */
2040 /***********************************************************************
2041 * SetVolumeLabelA (KERNEL32.@)
2043 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
2045 UNICODE_STRING rootW, volnameW;
2048 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2049 else rootW.Buffer = NULL;
2050 if (volname) RtlCreateUnicodeStringFromAsciiz(&volnameW, volname);
2051 else volnameW.Buffer = NULL;
2053 ret = SetVolumeLabelW( rootW.Buffer, volnameW.Buffer );
2055 RtlFreeUnicodeString(&rootW);
2056 RtlFreeUnicodeString(&volnameW);