Roll loop back up to avoid code duplication.
[wine.git] / files / drive.c
blob4ca9a284217cf1a79f7d19b584b6cbe9897b9352
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 #ifdef HAVE_UNISTD_H
39 # include <unistd.h>
40 #endif
42 #ifdef HAVE_SYS_PARAM_H
43 # include <sys/param.h>
44 #endif
45 #ifdef STATFS_DEFINED_BY_SYS_VFS
46 # include <sys/vfs.h>
47 #else
48 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
49 # include <sys/mount.h>
50 # else
51 # ifdef STATFS_DEFINED_BY_SYS_STATFS
52 # include <sys/statfs.h>
53 # endif
54 # endif
55 #endif
57 #define NONAMELESSUNION
58 #define NONAMELESSSTRUCT
59 #include "winbase.h"
60 #include "winternl.h"
61 #include "wine/winbase16.h" /* for GetCurrentTask */
62 #include "winerror.h"
63 #include "winioctl.h"
64 #include "ntddstor.h"
65 #include "ntddcdrm.h"
66 #include "drive.h"
67 #include "file.h"
68 #include "msdos.h"
69 #include "task.h"
70 #include "wine/unicode.h"
71 #include "wine/library.h"
72 #include "wine/server.h"
73 #include "wine/debug.h"
75 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
76 WINE_DECLARE_DEBUG_CHANNEL(file);
78 typedef struct
80 char *root; /* root dir in Unix format without trailing / */
81 LPWSTR dos_cwd; /* cwd in DOS format without leading or trailing \ */
82 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
83 char *device; /* raw device path */
84 WCHAR label_conf[12]; /* drive label as cfg'd in wine config */
85 WCHAR label_read[12]; /* drive label as read from device */
86 DWORD serial_conf; /* drive serial number as cfg'd in wine config */
87 UINT type; /* drive type */
88 UINT flags; /* drive flags */
89 UINT codepage; /* drive code page */
90 dev_t dev; /* unix device number */
91 ino_t ino; /* unix inode number */
92 } DOSDRIVE;
95 static const WCHAR DRIVE_Types[][8] =
97 { 0 }, /* DRIVE_UNKNOWN */
98 { 0 }, /* DRIVE_NO_ROOT_DIR */
99 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
100 {'h','d',0}, /* DRIVE_FIXED */
101 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
102 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
103 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
107 /* Known filesystem types */
109 typedef struct
111 const WCHAR name[6];
112 UINT flags;
113 } FS_DESCR;
115 static const FS_DESCR DRIVE_Filesystems[] =
117 { {'u','n','i','x',0}, DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
118 { {'m','s','d','o','s',0}, DRIVE_SHORT_NAMES },
119 { {'d','o','s',0}, DRIVE_SHORT_NAMES },
120 { {'f','a','t',0}, DRIVE_SHORT_NAMES },
121 { {'v','f','a','t',0}, DRIVE_CASE_PRESERVING },
122 { {'w','i','n','9','5',0}, DRIVE_CASE_PRESERVING },
123 { { 0 }, 0 }
127 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
128 static int DRIVE_CurDrive = -1;
130 static HTASK16 DRIVE_LastTask = 0;
132 /* strdup on the process heap */
133 inline static char *heap_strdup( const char *str )
135 INT len = strlen(str) + 1;
136 LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
137 if (p) memcpy( p, str, len );
138 return p;
141 extern void CDROM_InitRegistry(int dev);
143 /***********************************************************************
144 * DRIVE_GetDriveType
146 static UINT DRIVE_GetDriveType( LPCWSTR name )
148 WCHAR buffer[20];
149 int i;
150 static const WCHAR TypeW[] = {'T','y','p','e',0};
151 static const WCHAR hdW[] = {'h','d',0};
153 PROFILE_GetWineIniString( name, TypeW, hdW, buffer, 20 );
154 if(!buffer[0])
155 strcpyW(buffer,hdW);
156 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
158 if (!strcmpiW( buffer, DRIVE_Types[i] )) return i;
160 MESSAGE("%s: unknown drive type %s, defaulting to 'hd'.\n",
161 debugstr_w(name), debugstr_w(buffer) );
162 return DRIVE_FIXED;
166 /***********************************************************************
167 * DRIVE_GetFSFlags
169 static UINT DRIVE_GetFSFlags( LPCWSTR name, LPCWSTR value )
171 const FS_DESCR *descr;
173 for (descr = DRIVE_Filesystems; *descr->name; descr++)
174 if (!strcmpiW( value, descr->name )) return descr->flags;
175 MESSAGE("%s: unknown filesystem type %s, defaulting to 'win95'.\n",
176 debugstr_w(name), debugstr_w(value) );
177 return DRIVE_CASE_PRESERVING;
181 /***********************************************************************
182 * DRIVE_Init
184 int DRIVE_Init(void)
186 int i, len, count = 0;
187 WCHAR name[] = {'D','r','i','v','e',' ','A',0};
188 WCHAR drive_env[] = {'=','A',':',0};
189 WCHAR path[MAX_PATHNAME_LEN];
190 WCHAR buffer[80];
191 struct stat drive_stat_buffer;
192 WCHAR *p;
193 DOSDRIVE *drive;
194 static const WCHAR PathW[] = {'P','a','t','h',0};
195 static const WCHAR empty_strW[] = { 0 };
196 static const WCHAR CodepageW[] = {'C','o','d','e','p','a','g','e',0};
197 static const WCHAR LabelW[] = {'L','a','b','e','l',0};
198 static const WCHAR SerialW[] = {'S','e','r','i','a','l',0};
199 static const WCHAR zeroW[] = {'0',0};
200 static const WCHAR def_serialW[] = {'1','2','3','4','5','6','7','8',0};
201 static const WCHAR FilesystemW[] = {'F','i','l','e','s','y','s','t','e','m',0};
202 static const WCHAR win95W[] = {'w','i','n','9','5',0};
203 static const WCHAR DeviceW[] = {'D','e','v','i','c','e',0};
204 static const WCHAR ReadVolInfoW[] = {'R','e','a','d','V','o','l','I','n','f','o',0};
205 static const WCHAR FailReadOnlyW[] = {'F','a','i','l','R','e','a','d','O','n','l','y',0};
206 static const WCHAR driveC_labelW[] = {'D','r','i','v','e',' ','C',' ',' ',' ',' ',0};
208 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
210 PROFILE_GetWineIniString( name, PathW, empty_strW, path, MAX_PATHNAME_LEN );
211 if (path[0])
213 /* Get the code page number */
214 PROFILE_GetWineIniString( name, CodepageW, zeroW, /* 0 == CP_ACP */
215 buffer, 80 );
216 drive->codepage = strtolW( buffer, NULL, 10 );
218 p = path + strlenW(path) - 1;
219 while ((p > path) && (*p == '/')) *p-- = '\0';
221 if (path[0] == '/')
223 len = WideCharToMultiByte(drive->codepage, 0, path, -1, NULL, 0, NULL, NULL);
224 drive->root = HeapAlloc(GetProcessHeap(), 0, len);
225 WideCharToMultiByte(drive->codepage, 0, path, -1, drive->root, len, NULL, NULL);
227 else
229 /* relative paths are relative to config dir */
230 const char *config = wine_get_config_dir();
231 len = strlen(config);
232 len += WideCharToMultiByte(drive->codepage, 0, path, -1, NULL, 0, NULL, NULL) + 2;
233 drive->root = HeapAlloc( GetProcessHeap(), 0, len );
234 len -= sprintf( drive->root, "%s/", config );
235 WideCharToMultiByte(drive->codepage, 0, path, -1, drive->root + strlen(drive->root), len, NULL, NULL);
238 if (stat( drive->root, &drive_stat_buffer ))
240 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
241 drive->root, strerror(errno), 'A' + i);
242 HeapFree( GetProcessHeap(), 0, drive->root );
243 drive->root = NULL;
244 continue;
246 if (!S_ISDIR(drive_stat_buffer.st_mode))
248 MESSAGE("%s is not a directory, ignoring drive %c:\n",
249 drive->root, 'A' + i );
250 HeapFree( GetProcessHeap(), 0, drive->root );
251 drive->root = NULL;
252 continue;
255 drive->dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(drive->dos_cwd[0]));
256 drive->unix_cwd = heap_strdup( "" );
257 drive->type = DRIVE_GetDriveType( name );
258 drive->device = NULL;
259 drive->flags = 0;
260 drive->dev = drive_stat_buffer.st_dev;
261 drive->ino = drive_stat_buffer.st_ino;
263 /* Get the drive label */
264 PROFILE_GetWineIniString( name, LabelW, empty_strW, drive->label_conf, 12 );
265 if ((len = strlenW(drive->label_conf)) < 11)
267 /* Pad label with spaces */
268 while(len < 11) drive->label_conf[len++] = ' ';
269 drive->label_conf[11] = '\0';
272 /* Get the serial number */
273 PROFILE_GetWineIniString( name, SerialW, def_serialW, buffer, 80 );
274 drive->serial_conf = strtoulW( buffer, NULL, 16 );
276 /* Get the filesystem type */
277 PROFILE_GetWineIniString( name, FilesystemW, win95W, buffer, 80 );
278 drive->flags = DRIVE_GetFSFlags( name, buffer );
280 /* Get the device */
281 PROFILE_GetWineIniString( name, DeviceW, empty_strW, buffer, 80 );
282 if (buffer[0])
284 int cd_fd;
285 len = WideCharToMultiByte(CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL);
286 drive->device = HeapAlloc(GetProcessHeap(), 0, len);
287 WideCharToMultiByte(drive->codepage, 0, buffer, -1, drive->device, len, NULL, NULL);
289 if (PROFILE_GetWineIniBool( name, ReadVolInfoW, 1))
290 drive->flags |= DRIVE_READ_VOL_INFO;
292 if (drive->type == DRIVE_CDROM)
294 if ((cd_fd = open(drive->device, O_RDONLY|O_NONBLOCK)) != -1)
296 CDROM_InitRegistry(cd_fd);
297 close(cd_fd);
302 /* Get the FailReadOnly flag */
303 if (PROFILE_GetWineIniBool( name, FailReadOnlyW, 0 ))
304 drive->flags |= DRIVE_FAIL_READ_ONLY;
306 /* Make the first hard disk the current drive */
307 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
308 DRIVE_CurDrive = i;
310 count++;
311 TRACE("%s: path=%s type=%s label=%s serial=%08lx "
312 "flags=%08x codepage=%u dev=%x ino=%x\n",
313 debugstr_w(name), drive->root, debugstr_w(DRIVE_Types[drive->type]),
314 debugstr_w(drive->label_conf), drive->serial_conf, drive->flags,
315 drive->codepage, (int)drive->dev, (int)drive->ino );
317 else WARN("%s: not defined\n", debugstr_w(name) );
320 if (!count)
322 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
323 /* Create a C drive pointing to Unix root dir */
324 DOSDrives[2].root = heap_strdup( "/" );
325 DOSDrives[2].dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DOSDrives[2].dos_cwd[0]));
326 DOSDrives[2].unix_cwd = heap_strdup( "" );
327 strcpyW( DOSDrives[2].label_conf, driveC_labelW );
328 DOSDrives[2].serial_conf = 12345678;
329 DOSDrives[2].type = DRIVE_FIXED;
330 DOSDrives[2].device = NULL;
331 DOSDrives[2].flags = 0;
332 DRIVE_CurDrive = 2;
335 /* Make sure the current drive is valid */
336 if (DRIVE_CurDrive == -1)
338 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
340 if (drive->root && !(drive->flags & DRIVE_DISABLED))
342 DRIVE_CurDrive = i;
343 break;
348 /* get current working directory info for all drives */
349 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
351 if (!GetEnvironmentVariableW(drive_env, path, MAX_PATHNAME_LEN)) continue;
352 /* sanity check */
353 if (toupperW(path[0]) != drive_env[1] || path[1] != ':') continue;
354 DRIVE_Chdir( i, path + 2 );
356 return 1;
360 /***********************************************************************
361 * DRIVE_IsValid
363 int DRIVE_IsValid( int drive )
365 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
366 return (DOSDrives[drive].root &&
367 !(DOSDrives[drive].flags & DRIVE_DISABLED));
371 /***********************************************************************
372 * DRIVE_GetCurrentDrive
374 int DRIVE_GetCurrentDrive(void)
376 TDB *pTask = TASK_GetCurrent();
377 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
378 return DRIVE_CurDrive;
382 /***********************************************************************
383 * DRIVE_SetCurrentDrive
385 int DRIVE_SetCurrentDrive( int drive )
387 TDB *pTask = TASK_GetCurrent();
388 if (!DRIVE_IsValid( drive ))
390 SetLastError( ERROR_INVALID_DRIVE );
391 return 0;
393 TRACE("%c:\n", 'A' + drive );
394 DRIVE_CurDrive = drive;
395 if (pTask) pTask->curdrive = drive | 0x80;
396 return 1;
400 /***********************************************************************
401 * DRIVE_FindDriveRoot
403 * Find a drive for which the root matches the beginning of the given path.
404 * This can be used to translate a Unix path into a drive + DOS path.
405 * Return value is the drive, or -1 on error. On success, path is modified
406 * to point to the beginning of the DOS path.
408 * Note: path must be in the encoding of the underlying Unix file system.
410 int DRIVE_FindDriveRoot( const char **path )
412 /* Starting with the full path, check if the device and inode match any of
413 * the wine 'drives'. If not then remove the last path component and try
414 * again. If the last component was a '..' then skip a normal component
415 * since it's a directory that's ascended back out of.
417 int drive, level, len;
418 char buffer[MAX_PATHNAME_LEN];
419 char *p;
420 struct stat st;
422 strcpy( buffer, *path );
423 while ((p = strchr( buffer, '\\' )) != NULL)
424 *p = '/';
425 len = strlen(buffer);
427 /* strip off trailing slashes */
428 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
430 for (;;)
432 /* Find the drive */
433 if (stat( buffer, &st ) == 0 && S_ISDIR( st.st_mode ))
435 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
437 if (!DOSDrives[drive].root ||
438 (DOSDrives[drive].flags & DRIVE_DISABLED))
439 continue;
441 if ((DOSDrives[drive].dev == st.st_dev) &&
442 (DOSDrives[drive].ino == st.st_ino))
444 if (len == 1) len = 0; /* preserve root slash in returned path */
445 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
446 *path, 'A' + drive, buffer, *path + len);
447 *path += len;
448 if (!**path) *path = "\\";
449 return drive;
453 if (len <= 1) return -1; /* reached root */
455 level = 0;
456 while (level < 1)
458 /* find start of the last path component */
459 while (len > 1 && buffer[len - 1] != '/') len--;
460 if (!buffer[len]) break; /* empty component -> reached root */
461 /* does removing it take us up a level? */
462 if (strcmp( buffer + len, "." ) != 0)
463 level += strcmp( buffer + len, ".." ) ? 1 : -1;
464 buffer[len] = 0;
465 /* strip off trailing slashes */
466 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
472 /***********************************************************************
473 * DRIVE_FindDriveRootW
475 * Unicode version of DRIVE_FindDriveRoot.
477 int DRIVE_FindDriveRootW( LPCWSTR *path )
479 int drive, rootdrive = -1;
480 char buffer[MAX_PATHNAME_LEN];
481 LPCWSTR p = *path;
482 int len, match_len = -1;
484 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
486 if (!DOSDrives[drive].root ||
487 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
489 WideCharToMultiByte(DOSDrives[drive].codepage, 0, *path, -1,
490 buffer, MAX_PATHNAME_LEN, NULL, NULL);
492 len = strlen(DOSDrives[drive].root);
493 if(strncmp(DOSDrives[drive].root, buffer, len))
494 continue;
495 if(len <= match_len) continue;
496 match_len = len;
497 rootdrive = drive;
498 p = *path + len;
501 if (rootdrive != -1)
503 *path = p;
504 TRACE("%s -> drive %c:, root='%s', name=%s\n",
505 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, debugstr_w(*path) );
507 return rootdrive;
511 /***********************************************************************
512 * DRIVE_GetRoot
514 const char * DRIVE_GetRoot( int drive )
516 if (!DRIVE_IsValid( drive )) return NULL;
517 return DOSDrives[drive].root;
521 /***********************************************************************
522 * DRIVE_GetDosCwd
524 LPCWSTR DRIVE_GetDosCwd( int drive )
526 TDB *pTask = TASK_GetCurrent();
527 if (!DRIVE_IsValid( drive )) return NULL;
529 /* Check if we need to change the directory to the new task. */
530 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
531 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
532 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
534 static const WCHAR rootW[] = {'\\',0};
535 WCHAR curdirW[MAX_PATH];
536 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
537 /* Perform the task-switch */
538 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
539 DRIVE_LastTask = GetCurrentTask();
541 return DOSDrives[drive].dos_cwd;
545 /***********************************************************************
546 * DRIVE_GetUnixCwd
548 const char * DRIVE_GetUnixCwd( int drive )
550 TDB *pTask = TASK_GetCurrent();
551 if (!DRIVE_IsValid( drive )) return NULL;
553 /* Check if we need to change the directory to the new task. */
554 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
555 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
556 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
558 static const WCHAR rootW[] = {'\\',0};
559 WCHAR curdirW[MAX_PATH];
560 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
561 /* Perform the task-switch */
562 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
563 DRIVE_LastTask = GetCurrentTask();
565 return DOSDrives[drive].unix_cwd;
569 /***********************************************************************
570 * DRIVE_GetDevice
572 const char * DRIVE_GetDevice( int drive )
574 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
577 /******************************************************************
578 * static WORD CDROM_Data_FindBestVoldesc
582 static WORD CDROM_Data_FindBestVoldesc(int fd)
584 BYTE cur_vd_type, max_vd_type = 0;
585 unsigned int offs, best_offs = 0, extra_offs = 0;
586 char sig[3];
588 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
590 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
591 * the volume label is displaced forward by 8
593 lseek(fd, offs + 11, SEEK_SET); /* check for non-ISO9660 signature */
594 read(fd, &sig, 3);
595 if ((sig[0] == 'R') && (sig[1] == 'O') && (sig[2]=='M'))
597 extra_offs = 8;
599 lseek(fd, offs + extra_offs, SEEK_SET);
600 read(fd, &cur_vd_type, 1);
601 if (cur_vd_type == 0xff) /* voldesc set terminator */
602 break;
603 if (cur_vd_type > max_vd_type)
605 max_vd_type = cur_vd_type;
606 best_offs = offs + extra_offs;
609 return best_offs;
612 /***********************************************************************
613 * DRIVE_ReadSuperblock
615 * NOTE
616 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
617 * to check, that they are writing on a FAT filesystem !
619 int DRIVE_ReadSuperblock (int drive, char * buff)
621 #define DRIVE_SUPER 96
622 int fd;
623 off_t offs;
624 int ret = 0;
626 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
627 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
629 struct stat st;
630 if (!DOSDrives[drive].device)
631 ERR("No device configured for drive %c: !\n", 'A'+drive);
632 else
633 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
634 (stat(DOSDrives[drive].device, &st)) ?
635 "not available or symlink not valid ?" : "no permission");
636 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
637 PROFILE_UsageWineIni();
638 return -1;
641 switch(DOSDrives[drive].type)
643 case DRIVE_REMOVABLE:
644 case DRIVE_FIXED:
645 offs = 0;
646 break;
647 case DRIVE_CDROM:
648 offs = CDROM_Data_FindBestVoldesc(fd);
649 break;
650 default:
651 offs = 0;
652 break;
655 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs))
657 ret = -4;
658 goto the_end;
660 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER)
662 ret = -2;
663 goto the_end;
666 switch(DOSDrives[drive].type)
668 case DRIVE_REMOVABLE:
669 case DRIVE_FIXED:
670 if ((buff[0x26]!=0x29) || /* Check for FAT present */
671 /* FIXME: do really all FAT have their name beginning with
672 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
673 memcmp( buff+0x36,"FAT",3))
675 ERR("The filesystem is not FAT !! (device=%s)\n",
676 DOSDrives[drive].device);
677 ret = -3;
678 goto the_end;
680 break;
681 case DRIVE_CDROM:
682 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
684 ret = -3;
685 goto the_end;
687 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
688 break;
689 default:
690 ret = -3;
691 goto the_end;
694 return close(fd);
695 the_end:
696 close(fd);
697 return ret;
701 /***********************************************************************
702 * DRIVE_WriteSuperblockEntry
704 * NOTE
705 * We are writing as little as possible (ie. not the whole SuperBlock)
706 * not to interfere with kernel. The drive can be mounted !
708 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
710 int fd;
712 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
714 ERR("Cannot open the device %s (for writing)\n",
715 DOSDrives[drive].device);
716 return -1;
718 if (lseek(fd,ofs,SEEK_SET)!=ofs)
720 ERR("lseek failed on device %s !\n",
721 DOSDrives[drive].device);
722 close(fd);
723 return -2;
725 if (write(fd,buff,len)!=len)
727 close(fd);
728 ERR("Cannot write on %s !\n",
729 DOSDrives[drive].device);
730 return -3;
732 return close (fd);
735 /******************************************************************
736 * static HANDLE CDROM_Open
740 static HANDLE CDROM_Open(int drive)
742 WCHAR root[] = {'\\','\\','.','\\','A',':',0};
743 root[4] += drive;
744 return CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
747 /**************************************************************************
748 * CDROM_Data_GetLabel [internal]
750 DWORD CDROM_Data_GetLabel(int drive, WCHAR *label)
752 #define LABEL_LEN 32+1
753 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
754 WORD offs = CDROM_Data_FindBestVoldesc(dev);
755 WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
756 DWORD unicode_id = 0;
758 if (offs)
760 if ((lseek(dev, offs+0x58, SEEK_SET) == offs+0x58)
761 && (read(dev, &unicode_id, 3) == 3))
763 int ver = (unicode_id & 0xff0000) >> 16;
765 if ((lseek(dev, offs+0x28, SEEK_SET) != offs+0x28)
766 || (read(dev, &label_read, LABEL_LEN) != LABEL_LEN))
767 goto failure;
769 close(dev);
770 if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
771 && ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
772 { /* yippee, unicode */
773 int i;
774 WORD ch;
775 for (i=0; i<LABEL_LEN;i++)
776 { /* Motorola -> Intel Unicode conversion :-\ */
777 ch = label_read[i];
778 label_read[i] = (ch << 8) | (ch >> 8);
780 strncpyW(label, label_read, 11);
781 label[11] = 0;
783 else
785 MultiByteToWideChar(DOSDrives[drive].codepage, 0, (LPSTR)label_read, -1, label, 11);
786 label[11] = '\0';
788 return 1;
791 failure:
792 close(dev);
793 ERR("error reading label !\n");
794 return 0;
797 /**************************************************************************
798 * CDROM_GetLabel [internal]
800 static DWORD CDROM_GetLabel(int drive, WCHAR *label)
802 HANDLE h = CDROM_Open(drive);
803 CDROM_DISK_DATA cdd;
804 DWORD br;
805 DWORD ret = 1;
807 if (!h || !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
808 return 0;
810 switch (cdd.DiskData & 0x03)
812 case CDROM_DISK_DATA_TRACK:
813 if (!CDROM_Data_GetLabel(drive, label))
814 ret = 0;
815 break;
816 case CDROM_DISK_AUDIO_TRACK:
818 static const WCHAR audioCD[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
819 strcpyW(label, audioCD);
820 break;
822 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
823 FIXME("Need to get the label of a mixed mode CD: not implemented yet !\n");
824 /* fall through */
825 case 0:
826 ret = 0;
827 break;
829 TRACE("CD: label is %s\n", debugstr_w(label));
831 return ret;
833 /***********************************************************************
834 * DRIVE_GetLabel
836 LPCWSTR DRIVE_GetLabel( int drive )
838 int read = 0;
839 char buff[DRIVE_SUPER];
840 int offs = -1;
842 if (!DRIVE_IsValid( drive )) return NULL;
843 if (DOSDrives[drive].type == DRIVE_CDROM)
845 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
847 else
848 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
850 if (DRIVE_ReadSuperblock(drive,(char *) buff))
851 ERR("Invalid or unreadable superblock on %s (%c:).\n",
852 DOSDrives[drive].device, (char)(drive+'A'));
853 else {
854 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
855 DOSDrives[drive].type == DRIVE_FIXED)
856 offs = 0x2b;
858 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
859 if (offs != -1)
860 MultiByteToWideChar(DOSDrives[drive].codepage, 0, buff+offs, 11,
861 DOSDrives[drive].label_read, 11);
862 DOSDrives[drive].label_read[11]='\0';
863 read = 1;
867 return (read) ?
868 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
871 #define CDFRAMES_PERSEC 75
872 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
873 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
874 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
876 /**************************************************************************
877 * CDROM_Audio_GetSerial [internal]
879 static DWORD CDROM_Audio_GetSerial(HANDLE h)
881 unsigned long serial = 0;
882 int i;
883 WORD wMagic;
884 DWORD dwStart, dwEnd, br;
885 CDROM_TOC toc;
887 if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
888 return 0;
891 * wMagic collects the wFrames from track 1
892 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
893 * frames.
894 * There it is collected for correcting the serial when there are less than
895 * 3 tracks.
897 wMagic = toc.TrackData[0].Address[2];
898 dwStart = FRAME_OF_TOC(toc, toc.FirstTrack);
900 for (i = 0; i <= toc.LastTrack - toc.FirstTrack; i++) {
901 serial += (toc.TrackData[i].Address[0] << 16) |
902 (toc.TrackData[i].Address[1] << 8) | toc.TrackData[i].Address[2];
904 dwEnd = FRAME_OF_TOC(toc, toc.LastTrack + 1);
906 if (toc.LastTrack - toc.FirstTrack + 1 < 3)
907 serial += wMagic + (dwEnd - dwStart);
909 return serial;
912 /**************************************************************************
913 * CDROM_Data_GetSerial [internal]
915 static DWORD CDROM_Data_GetSerial(int drive)
917 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
918 WORD offs;
919 union {
920 unsigned long val;
921 unsigned char p[4];
922 } serial;
923 BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
926 if (dev == -1) return 0;
927 offs = CDROM_Data_FindBestVoldesc(dev);
929 serial.val = 0;
930 if (offs)
932 BYTE buf[2048];
933 OSVERSIONINFOA ovi;
934 int i;
936 lseek(dev, offs, SEEK_SET);
937 read(dev, buf, 2048);
939 * OK, another braindead one... argh. Just believe it.
940 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
941 * It's true and nobody will ever be able to change it.
943 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
944 GetVersionExA(&ovi);
945 if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (ovi.dwMajorVersion >= 4))
947 b0 = 3; b1 = 2; b2 = 1; b3 = 0;
949 for (i = 0; i < 2048; i += 4)
951 /* DON'T optimize this into DWORD !! (breaks overflow) */
952 serial.p[b0] += buf[i+b0];
953 serial.p[b1] += buf[i+b1];
954 serial.p[b2] += buf[i+b2];
955 serial.p[b3] += buf[i+b3];
958 close(dev);
959 return serial.val;
962 /**************************************************************************
963 * CDROM_GetSerial [internal]
965 static DWORD CDROM_GetSerial(int drive)
967 DWORD serial = 0;
968 HANDLE h = CDROM_Open(drive);
969 CDROM_DISK_DATA cdd;
970 DWORD br;
972 if (!h || ! !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
973 return 0;
975 switch (cdd.DiskData & 0x03)
977 case CDROM_DISK_DATA_TRACK:
978 /* hopefully a data CD */
979 serial = CDROM_Data_GetSerial(drive);
980 break;
981 case CDROM_DISK_AUDIO_TRACK:
982 /* fall thru */
983 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
984 serial = CDROM_Audio_GetSerial(h);
985 break;
986 case 0:
987 break;
990 if (serial)
991 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
993 CloseHandle(h);
995 return serial;
998 /***********************************************************************
999 * DRIVE_GetSerialNumber
1001 DWORD DRIVE_GetSerialNumber( int drive )
1003 DWORD serial = 0;
1004 char buff[DRIVE_SUPER];
1006 TRACE("drive %d, type = %d\n", drive, DOSDrives[drive].type);
1008 if (!DRIVE_IsValid( drive )) return 0;
1010 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1012 switch(DOSDrives[drive].type)
1014 case DRIVE_REMOVABLE:
1015 case DRIVE_FIXED:
1016 if (DRIVE_ReadSuperblock(drive,(char *) buff))
1017 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
1018 " Maybe not FAT?\n" ,
1019 DOSDrives[drive].device, 'A'+drive);
1020 else
1021 serial = *((DWORD*)(buff+0x27));
1022 break;
1023 case DRIVE_CDROM:
1024 serial = CDROM_GetSerial(drive);
1025 break;
1026 default:
1027 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
1031 return (serial) ? serial : DOSDrives[drive].serial_conf;
1035 /***********************************************************************
1036 * DRIVE_SetSerialNumber
1038 int DRIVE_SetSerialNumber( int drive, DWORD serial )
1040 char buff[DRIVE_SUPER];
1042 if (!DRIVE_IsValid( drive )) return 0;
1044 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1046 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
1047 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
1048 /* check, if the drive has a FAT filesystem */
1049 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
1050 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
1051 return 1;
1054 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
1055 DOSDrives[drive].serial_conf = serial;
1056 return 1;
1060 /***********************************************************************
1061 * DRIVE_GetType
1063 static UINT DRIVE_GetType( int drive )
1065 if (!DRIVE_IsValid( drive )) return DRIVE_NO_ROOT_DIR;
1066 return DOSDrives[drive].type;
1070 /***********************************************************************
1071 * DRIVE_GetFlags
1073 UINT DRIVE_GetFlags( int drive )
1075 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1076 return DOSDrives[drive].flags;
1079 /***********************************************************************
1080 * DRIVE_GetCodepage
1082 UINT DRIVE_GetCodepage( int drive )
1084 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1085 return DOSDrives[drive].codepage;
1089 /***********************************************************************
1090 * DRIVE_Chdir
1092 int DRIVE_Chdir( int drive, LPCWSTR path )
1094 DOS_FULL_NAME full_name;
1095 WCHAR buffer[MAX_PATHNAME_LEN];
1096 LPSTR unix_cwd;
1097 BY_HANDLE_FILE_INFORMATION info;
1098 TDB *pTask = TASK_GetCurrent();
1100 buffer[0] = 'A' + drive;
1101 buffer[1] = ':';
1102 buffer[2] = 0;
1103 TRACE("(%s,%s)\n", debugstr_w(buffer), debugstr_w(path) );
1104 strncpyW( buffer + 2, path, MAX_PATHNAME_LEN - 2 );
1105 buffer[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1107 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
1108 if (!FILE_Stat( full_name.long_name, &info, NULL )) return 0;
1109 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1111 SetLastError( ERROR_FILE_NOT_FOUND );
1112 return 0;
1114 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
1115 while (*unix_cwd == '/') unix_cwd++;
1117 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1118 'A' + drive, unix_cwd, debugstr_w(full_name.short_name + 3) );
1120 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
1121 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
1122 DOSDrives[drive].dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name.short_name) - 2) * sizeof(WCHAR));
1123 strcpyW(DOSDrives[drive].dos_cwd, full_name.short_name + 3);
1124 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
1126 if (pTask && (pTask->curdrive & 0x80) &&
1127 ((pTask->curdrive & ~0x80) == drive))
1129 WideCharToMultiByte(CP_ACP, 0, full_name.short_name + 2, -1,
1130 pTask->curdir, sizeof(pTask->curdir), NULL, NULL);
1131 DRIVE_LastTask = GetCurrentTask();
1133 return 1;
1137 /***********************************************************************
1138 * DRIVE_Disable
1140 int DRIVE_Disable( int drive )
1142 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1144 SetLastError( ERROR_INVALID_DRIVE );
1145 return 0;
1147 DOSDrives[drive].flags |= DRIVE_DISABLED;
1148 return 1;
1152 /***********************************************************************
1153 * DRIVE_Enable
1155 int DRIVE_Enable( int drive )
1157 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1159 SetLastError( ERROR_INVALID_DRIVE );
1160 return 0;
1162 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1163 return 1;
1167 /***********************************************************************
1168 * DRIVE_SetLogicalMapping
1170 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
1172 /* If new_drive is already valid, do nothing and return 0
1173 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1175 DOSDRIVE *old, *new;
1177 old = DOSDrives + existing_drive;
1178 new = DOSDrives + new_drive;
1180 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
1181 !old->root ||
1182 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
1184 SetLastError( ERROR_INVALID_DRIVE );
1185 return 0;
1188 if ( new->root )
1190 TRACE("Can't map drive %c: to already existing drive %c:\n",
1191 'A' + existing_drive, 'A' + new_drive );
1192 /* it is already mapped there, so return success */
1193 if (!strcmp(old->root,new->root))
1194 return 1;
1195 return 0;
1198 new->root = heap_strdup( old->root );
1199 new->dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(old->dos_cwd) + 1) * sizeof(WCHAR));
1200 strcpyW(new->dos_cwd, old->dos_cwd);
1201 new->unix_cwd = heap_strdup( old->unix_cwd );
1202 new->device = heap_strdup( old->device );
1203 memcpy ( new->label_conf, old->label_conf, 12 );
1204 memcpy ( new->label_read, old->label_read, 12 );
1205 new->serial_conf = old->serial_conf;
1206 new->type = old->type;
1207 new->flags = old->flags;
1208 new->dev = old->dev;
1209 new->ino = old->ino;
1211 TRACE("Drive %c: is now equal to drive %c:\n",
1212 'A' + new_drive, 'A' + existing_drive );
1214 return 1;
1218 /***********************************************************************
1219 * DRIVE_OpenDevice
1221 * Open the drive raw device and return a Unix fd (or -1 on error).
1223 int DRIVE_OpenDevice( int drive, int flags )
1225 if (!DRIVE_IsValid( drive )) return -1;
1226 return open( DOSDrives[drive].device, flags );
1230 /***********************************************************************
1231 * DRIVE_RawRead
1233 * Read raw sectors from a device
1235 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1237 int fd;
1239 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1241 lseek( fd, begin * 512, SEEK_SET );
1242 /* FIXME: check errors */
1243 read( fd, dataptr, nr_sect * 512 );
1244 close( fd );
1246 else
1248 memset(dataptr, 0, nr_sect * 512);
1249 if (fake_success)
1251 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
1252 if (begin == 1) *dataptr = 0xf8;
1254 else
1255 return 0;
1257 return 1;
1261 /***********************************************************************
1262 * DRIVE_RawWrite
1264 * Write raw sectors to a device
1266 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1268 int fd;
1270 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1272 lseek( fd, begin * 512, SEEK_SET );
1273 /* FIXME: check errors */
1274 write( fd, dataptr, nr_sect * 512 );
1275 close( fd );
1277 else
1278 if (!(fake_success))
1279 return 0;
1281 return 1;
1285 /***********************************************************************
1286 * DRIVE_GetFreeSpace
1288 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
1289 PULARGE_INTEGER available )
1291 struct statfs info;
1293 if (!DRIVE_IsValid(drive))
1295 SetLastError( ERROR_PATH_NOT_FOUND );
1296 return 0;
1299 /* FIXME: add autoconf check for this */
1300 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1301 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
1302 #else
1303 if (statfs( DOSDrives[drive].root, &info) < 0)
1304 #endif
1306 FILE_SetDosError();
1307 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
1308 return 0;
1311 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
1312 #ifdef STATFS_HAS_BAVAIL
1313 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
1314 #else
1315 # ifdef STATFS_HAS_BFREE
1316 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
1317 # else
1318 # error "statfs has no bfree/bavail member!"
1319 # endif
1320 #endif
1321 if (DOSDrives[drive].type == DRIVE_CDROM)
1322 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1323 available->QuadPart = 0;
1325 return 1;
1328 /***********************************************************************
1329 * DRIVE_GetCurrentDirectory
1330 * Returns "X:\\path\\etc\\".
1332 * Despite the API description, return required length including the
1333 * terminating null when buffer too small. This is the real behaviour.
1335 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPWSTR buf )
1337 UINT ret;
1338 LPCWSTR dos_cwd = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1339 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1341 ret = strlenW(dos_cwd) + 3; /* length of WHOLE current directory */
1342 if (ret >= buflen) return ret + 1;
1344 strcpyW( buf, driveA_rootW );
1345 buf[0] += DRIVE_GetCurrentDrive();
1346 strcatW( buf, dos_cwd );
1347 return ret;
1351 /***********************************************************************
1352 * DRIVE_BuildEnv
1354 * Build the environment array containing the drives' current directories.
1355 * Resulting pointer must be freed with HeapFree.
1357 char *DRIVE_BuildEnv(void)
1359 int i, length = 0;
1360 LPCWSTR cwd[MAX_DOS_DRIVES];
1361 char *env, *p;
1363 for (i = 0; i < MAX_DOS_DRIVES; i++)
1365 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0])
1366 length += WideCharToMultiByte(DRIVE_GetCodepage(i), 0,
1367 cwd[i], -1, NULL, 0, NULL, NULL) + 7;
1369 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
1370 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
1372 if (cwd[i] && cwd[i][0])
1374 *p++ = '='; *p++ = 'A' + i; *p++ = ':';
1375 *p++ = '='; *p++ = 'A' + i; *p++ = ':'; *p++ = '\\';
1376 WideCharToMultiByte(DRIVE_GetCodepage(i), 0, cwd[i], -1, p, 0x7fffffff, NULL, NULL);
1377 p += strlen(p) + 1;
1380 *p = 0;
1381 return env;
1385 /***********************************************************************
1386 * GetDiskFreeSpace (KERNEL.422)
1388 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
1389 LPDWORD sector_bytes, LPDWORD free_clusters,
1390 LPDWORD total_clusters )
1392 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
1393 free_clusters, total_clusters );
1397 /***********************************************************************
1398 * GetDiskFreeSpaceW (KERNEL32.@)
1400 * Fails if expression resulting from current drive's dir and "root"
1401 * is not a root dir of the target drive.
1403 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1404 * if the corresponding info is unneeded.
1406 * FIXME: needs to support UNC names from Win95 OSR2 on.
1408 * Behaviour under Win95a:
1409 * CurrDir root result
1410 * "E:\\TEST" "E:" FALSE
1411 * "E:\\" "E:" TRUE
1412 * "E:\\" "E" FALSE
1413 * "E:\\" "\\" TRUE
1414 * "E:\\TEST" "\\" TRUE
1415 * "E:\\TEST" ":\\" FALSE
1416 * "E:\\TEST" "E:\\" TRUE
1417 * "E:\\TEST" "" FALSE
1418 * "E:\\" "" FALSE (!)
1419 * "E:\\" 0x0 TRUE
1420 * "E:\\TEST" 0x0 TRUE (!)
1421 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1422 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1424 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1425 LPDWORD sector_bytes, LPDWORD free_clusters,
1426 LPDWORD total_clusters )
1428 int drive, sec_size;
1429 ULARGE_INTEGER size,available;
1430 LPCWSTR path;
1431 DWORD cluster_sec;
1433 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root), cluster_sectors, sector_bytes,
1434 free_clusters, total_clusters);
1436 if (!root || root[0] == '\\' || root[0] == '/')
1437 drive = DRIVE_GetCurrentDrive();
1438 else
1439 if (root[0] && root[1] == ':') /* root contains drive tag */
1441 drive = toupperW(root[0]) - 'A';
1442 path = &root[2];
1443 if (path[0] == '\0')
1445 path = DRIVE_GetDosCwd(drive);
1446 if (!path)
1448 SetLastError(ERROR_PATH_NOT_FOUND);
1449 return FALSE;
1452 else
1453 if (path[0] == '\\')
1454 path++;
1456 if (path[0]) /* oops, we are in a subdir */
1458 SetLastError(ERROR_INVALID_NAME);
1459 return FALSE;
1462 else
1464 if (!root[0])
1465 SetLastError(ERROR_PATH_NOT_FOUND);
1466 else
1467 SetLastError(ERROR_INVALID_NAME);
1468 return FALSE;
1471 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1473 /* Cap the size and available at 2GB as per specs. */
1474 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1476 size.s.HighPart = 0;
1477 size.s.LowPart = 0x7fffffff;
1479 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1481 available.s.HighPart =0;
1482 available.s.LowPart = 0x7fffffff;
1484 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1485 size.s.LowPart /= sec_size;
1486 available.s.LowPart /= sec_size;
1487 /* FIXME: probably have to adjust those variables too for CDFS */
1488 cluster_sec = 1;
1489 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1491 if (cluster_sectors)
1492 *cluster_sectors = cluster_sec;
1493 if (sector_bytes)
1494 *sector_bytes = sec_size;
1495 if (free_clusters)
1496 *free_clusters = available.s.LowPart / cluster_sec;
1497 if (total_clusters)
1498 *total_clusters = size.s.LowPart / cluster_sec;
1499 return TRUE;
1503 /***********************************************************************
1504 * GetDiskFreeSpaceA (KERNEL32.@)
1506 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1507 LPDWORD sector_bytes, LPDWORD free_clusters,
1508 LPDWORD total_clusters )
1510 UNICODE_STRING rootW;
1511 BOOL ret = FALSE;
1513 if (root)
1515 if(!RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1517 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1518 return FALSE;
1521 else
1522 rootW.Buffer = NULL;
1524 ret = GetDiskFreeSpaceW(rootW.Buffer, cluster_sectors, sector_bytes,
1525 free_clusters, total_clusters );
1526 RtlFreeUnicodeString(&rootW);
1528 return ret;
1532 /***********************************************************************
1533 * GetDiskFreeSpaceExW (KERNEL32.@)
1535 * This function is used to acquire the size of the available and
1536 * total space on a logical volume.
1538 * RETURNS
1540 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1541 * detailed error information.
1544 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root,
1545 PULARGE_INTEGER avail,
1546 PULARGE_INTEGER total,
1547 PULARGE_INTEGER totalfree)
1549 int drive;
1550 ULARGE_INTEGER size,available;
1552 if (!root) drive = DRIVE_GetCurrentDrive();
1553 else
1554 { /* C: always works for GetDiskFreeSpaceEx */
1555 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
1557 FIXME("there are valid root names which are not supported yet\n");
1558 /* ..like UNC names, for instance. */
1560 WARN("invalid root '%s'\n", debugstr_w(root));
1561 return FALSE;
1563 drive = toupperW(root[0]) - 'A';
1566 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1568 if (total)
1570 total->s.HighPart = size.s.HighPart;
1571 total->s.LowPart = size.s.LowPart;
1574 if (totalfree)
1576 totalfree->s.HighPart = available.s.HighPart;
1577 totalfree->s.LowPart = available.s.LowPart;
1580 if (avail)
1582 if (FIXME_ON(dosfs))
1584 /* On Windows2000, we need to check the disk quota
1585 allocated for the user owning the calling process. We
1586 don't want to be more obtrusive than necessary with the
1587 FIXME messages, so don't print the FIXME unless Wine is
1588 actually masquerading as Windows2000. */
1590 OSVERSIONINFOA ovi;
1591 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1592 if (GetVersionExA(&ovi))
1594 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1595 FIXME("no per-user quota support yet\n");
1599 /* Quick hack, should eventually be fixed to work 100% with
1600 Windows2000 (see comment above). */
1601 avail->s.HighPart = available.s.HighPart;
1602 avail->s.LowPart = available.s.LowPart;
1605 return TRUE;
1608 /***********************************************************************
1609 * GetDiskFreeSpaceExA (KERNEL32.@)
1611 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
1612 PULARGE_INTEGER total,
1613 PULARGE_INTEGER totalfree)
1615 UNICODE_STRING rootW;
1616 BOOL ret;
1618 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
1619 else rootW.Buffer = NULL;
1621 ret = GetDiskFreeSpaceExW( rootW.Buffer, avail, total, totalfree);
1623 RtlFreeUnicodeString(&rootW);
1624 return ret;
1627 /***********************************************************************
1628 * GetDriveType (KERNEL.136)
1629 * This function returns the type of a drive in Win16.
1630 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1631 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1632 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1633 * do any pseudo-clever changes.
1635 * RETURNS
1636 * drivetype DRIVE_xxx
1638 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1640 UINT type = DRIVE_GetType(drive);
1641 TRACE("(%c:)\n", 'A' + drive );
1642 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1643 return type;
1647 /***********************************************************************
1648 * GetDriveTypeW (KERNEL32.@)
1650 * Returns the type of the disk drive specified. If root is NULL the
1651 * root of the current directory is used.
1653 * RETURNS
1655 * Type of drive (from Win32 SDK):
1657 * DRIVE_UNKNOWN unable to find out anything about the drive
1658 * DRIVE_NO_ROOT_DIR nonexistent root dir
1659 * DRIVE_REMOVABLE the disk can be removed from the machine
1660 * DRIVE_FIXED the disk can not be removed from the machine
1661 * DRIVE_REMOTE network disk
1662 * DRIVE_CDROM CDROM drive
1663 * DRIVE_RAMDISK virtual disk in RAM
1665 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1667 int drive;
1668 TRACE("(%s)\n", debugstr_w(root));
1670 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1671 else
1673 if ((root[1]) && (root[1] != ':'))
1675 WARN("invalid root %s\n", debugstr_w(root));
1676 return DRIVE_NO_ROOT_DIR;
1678 drive = toupperW(root[0]) - 'A';
1680 return DRIVE_GetType(drive);
1684 /***********************************************************************
1685 * GetDriveTypeA (KERNEL32.@)
1687 UINT WINAPI GetDriveTypeA( LPCSTR root )
1689 UNICODE_STRING rootW;
1690 UINT ret = 0;
1692 if (root)
1694 if( !RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1696 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1697 return 0;
1700 else
1701 rootW.Buffer = NULL;
1703 ret = GetDriveTypeW(rootW.Buffer);
1705 RtlFreeUnicodeString(&rootW);
1706 return ret;
1711 /***********************************************************************
1712 * GetCurrentDirectory (KERNEL.411)
1714 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1716 WCHAR cur_dirW[MAX_PATH];
1718 DRIVE_GetCurrentDirectory(MAX_PATH, cur_dirW);
1719 return (UINT16)WideCharToMultiByte(CP_ACP, 0, cur_dirW, -1, buf, buflen, NULL, NULL);
1723 /***********************************************************************
1724 * GetCurrentDirectoryW (KERNEL32.@)
1726 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1728 UINT ret;
1729 WCHAR longname[MAX_PATHNAME_LEN];
1730 WCHAR shortname[MAX_PATHNAME_LEN];
1732 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1733 if ( ret > MAX_PATHNAME_LEN ) {
1734 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1735 return ret;
1737 GetLongPathNameW(shortname, longname, MAX_PATHNAME_LEN);
1738 ret = strlenW( longname ) + 1;
1739 if (ret > buflen) return ret;
1740 strcpyW(buf, longname);
1741 return ret - 1;
1744 /***********************************************************************
1745 * GetCurrentDirectoryA (KERNEL32.@)
1747 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1749 WCHAR bufferW[MAX_PATH];
1750 DWORD ret, retW;
1752 retW = GetCurrentDirectoryW(MAX_PATH, bufferW);
1754 if (!retW)
1755 ret = 0;
1756 else if (retW > MAX_PATH)
1758 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1759 ret = 0;
1761 else
1763 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1764 if (buflen >= ret)
1766 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, buflen, NULL, NULL);
1767 ret--; /* length without 0 */
1770 return ret;
1774 /***********************************************************************
1775 * SetCurrentDirectory (KERNEL.412)
1777 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1779 return SetCurrentDirectoryA( dir );
1783 /***********************************************************************
1784 * SetCurrentDirectoryW (KERNEL32.@)
1786 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dir )
1788 int drive, olddrive = DRIVE_GetCurrentDrive();
1790 if (!dir)
1792 SetLastError(ERROR_INVALID_PARAMETER);
1793 return FALSE;
1795 if (dir[0] && (dir[1]==':'))
1797 drive = toupperW( *dir ) - 'A';
1798 dir += 2;
1800 else
1801 drive = olddrive;
1803 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1804 sets pTask->curdir only if pTask->curdrive is drive */
1805 if (!(DRIVE_SetCurrentDrive( drive )))
1806 return FALSE;
1808 /* FIXME: what about empty strings? Add a \\ ? */
1809 if (!DRIVE_Chdir( drive, dir )) {
1810 DRIVE_SetCurrentDrive(olddrive);
1811 return FALSE;
1813 return TRUE;
1817 /***********************************************************************
1818 * SetCurrentDirectoryA (KERNEL32.@)
1820 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1822 UNICODE_STRING dirW;
1823 BOOL ret = FALSE;
1825 if (!dir)
1827 SetLastError(ERROR_INVALID_PARAMETER);
1828 return FALSE;
1831 if (RtlCreateUnicodeStringFromAsciiz(&dirW, dir))
1833 ret = SetCurrentDirectoryW(dirW.Buffer);
1834 RtlFreeUnicodeString(&dirW);
1836 else
1837 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1838 return ret;
1842 /***********************************************************************
1843 * GetLogicalDriveStringsA (KERNEL32.@)
1845 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1847 int drive, count;
1849 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1850 if (DRIVE_IsValid(drive)) count++;
1851 if ((count * 4) + 1 <= len)
1853 LPSTR p = buffer;
1854 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1855 if (DRIVE_IsValid(drive))
1857 *p++ = 'a' + drive;
1858 *p++ = ':';
1859 *p++ = '\\';
1860 *p++ = '\0';
1862 *p = '\0';
1863 return count * 4;
1865 else
1866 return (count * 4) + 1; /* account for terminating null */
1867 /* The API tells about these different return values */
1871 /***********************************************************************
1872 * GetLogicalDriveStringsW (KERNEL32.@)
1874 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1876 int drive, count;
1878 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1879 if (DRIVE_IsValid(drive)) count++;
1880 if (count * 4 * sizeof(WCHAR) <= len)
1882 LPWSTR p = buffer;
1883 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1884 if (DRIVE_IsValid(drive))
1886 *p++ = (WCHAR)('a' + drive);
1887 *p++ = (WCHAR)':';
1888 *p++ = (WCHAR)'\\';
1889 *p++ = (WCHAR)'\0';
1891 *p = (WCHAR)'\0';
1893 return count * 4 * sizeof(WCHAR);
1897 /***********************************************************************
1898 * GetLogicalDrives (KERNEL32.@)
1900 DWORD WINAPI GetLogicalDrives(void)
1902 DWORD ret = 0;
1903 int drive;
1905 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1907 if ( (DRIVE_IsValid(drive)) ||
1908 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1909 ret |= (1 << drive);
1911 return ret;
1915 /***********************************************************************
1916 * GetVolumeInformationW (KERNEL32.@)
1918 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1919 DWORD label_len, DWORD *serial,
1920 DWORD *filename_len, DWORD *flags,
1921 LPWSTR fsname, DWORD fsname_len )
1923 int drive;
1924 LPWSTR cp;
1926 /* FIXME, SetLastError()s missing */
1928 if (!root) drive = DRIVE_GetCurrentDrive();
1929 else
1931 if (root[0] && root[1] != ':')
1933 WARN("invalid root %s\n", debugstr_w(root));
1934 return FALSE;
1936 drive = toupperW(root[0]) - 'A';
1938 if (!DRIVE_IsValid( drive )) return FALSE;
1939 if (label && label_len)
1941 strncpyW( label, DRIVE_GetLabel(drive), label_len );
1942 label[label_len - 1] = 0; /* ensure 0 termination */
1943 cp = label + strlenW(label);
1944 while (cp != label && *(cp-1) == ' ') cp--;
1945 *cp = '\0';
1947 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1949 /* Set the filesystem information */
1950 /* Note: we only emulate a FAT fs at present */
1952 if (filename_len) {
1953 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1954 *filename_len = 12;
1955 else
1956 *filename_len = 255;
1958 if (flags)
1960 *flags=0;
1961 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1962 *flags|=FS_CASE_SENSITIVE;
1963 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1964 *flags|=FS_CASE_IS_PRESERVED;
1966 if (fsname && fsname_len)
1968 /* Diablo checks that return code ... */
1969 if (DOSDrives[drive].type == DRIVE_CDROM)
1971 static const WCHAR cdfsW[] = {'C','D','F','S',0};
1972 strncpyW( fsname, cdfsW, fsname_len );
1974 else
1976 static const WCHAR fatW[] = {'F','A','T',0};
1977 strncpyW( fsname, fatW, fsname_len );
1979 fsname[fsname_len - 1] = 0; /* ensure 0 termination */
1981 return TRUE;
1985 /***********************************************************************
1986 * GetVolumeInformationA (KERNEL32.@)
1988 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1989 DWORD label_len, DWORD *serial,
1990 DWORD *filename_len, DWORD *flags,
1991 LPSTR fsname, DWORD fsname_len )
1993 UNICODE_STRING rootW;
1994 LPWSTR labelW, fsnameW;
1995 BOOL ret;
1997 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
1998 else rootW.Buffer = NULL;
1999 labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
2000 fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
2002 if ((ret = GetVolumeInformationW(rootW.Buffer, labelW, label_len, serial,
2003 filename_len, flags, fsnameW, fsname_len)))
2005 if (label) WideCharToMultiByte(CP_ACP, 0, labelW, -1, label, label_len, NULL, NULL);
2006 if (fsname) WideCharToMultiByte(CP_ACP, 0, fsnameW, -1, fsname, fsname_len, NULL, NULL);
2009 RtlFreeUnicodeString(&rootW);
2010 if (labelW) HeapFree( GetProcessHeap(), 0, labelW );
2011 if (fsnameW) HeapFree( GetProcessHeap(), 0, fsnameW );
2012 return ret;
2015 /***********************************************************************
2016 * SetVolumeLabelW (KERNEL32.@)
2018 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR volname )
2020 int drive;
2022 /* FIXME, SetLastErrors missing */
2024 if (!root) drive = DRIVE_GetCurrentDrive();
2025 else
2027 if ((root[1]) && (root[1] != ':'))
2029 WARN("invalid root %s\n", debugstr_w(root));
2030 return FALSE;
2032 drive = toupperW(root[0]) - 'A';
2034 if (!DRIVE_IsValid( drive )) return FALSE;
2036 /* some copy protection stuff check this */
2037 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
2039 strncpyW(DOSDrives[drive].label_conf, volname, 12);
2040 DOSDrives[drive].label_conf[12 - 1] = 0; /* ensure 0 termination */
2041 return TRUE;
2044 /***********************************************************************
2045 * SetVolumeLabelA (KERNEL32.@)
2047 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
2049 UNICODE_STRING rootW, volnameW;
2050 BOOL ret;
2052 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2053 else rootW.Buffer = NULL;
2054 if (volname) RtlCreateUnicodeStringFromAsciiz(&volnameW, volname);
2055 else volnameW.Buffer = NULL;
2057 ret = SetVolumeLabelW( rootW.Buffer, volnameW.Buffer );
2059 RtlFreeUnicodeString(&rootW);
2060 RtlFreeUnicodeString(&volnameW);
2061 return ret;
2064 /***********************************************************************
2065 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
2067 DWORD WINAPI GetVolumeNameForVolumeMountPointW(LPWSTR str, DWORD a, DWORD b)
2069 FIXME("(%s, %lx, %lx): stub\n", debugstr_w(str), a, b);
2070 return 0;