Handle errors in std marshaller better, do not crash.
[wine.git] / files / drive.c
blobf13bc53852fd6130a6143d41e868de9de03c80fd
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 #include "winbase.h"
58 #include "winternl.h"
59 #include "wine/winbase16.h" /* for GetCurrentTask */
60 #include "winerror.h"
61 #include "winioctl.h"
62 #include "ntddstor.h"
63 #include "ntddcdrm.h"
64 #include "drive.h"
65 #include "file.h"
66 #include "heap.h"
67 #include "msdos.h"
68 #include "task.h"
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);
77 typedef struct
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 */
91 } DOSDRIVE;
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 */
108 typedef struct
110 const WCHAR name[6];
111 UINT flags;
112 } FS_DESCR;
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 },
122 { { 0 }, 0 }
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 );
137 return p;
140 extern void CDROM_InitRegistry(int dev);
142 /***********************************************************************
143 * DRIVE_GetDriveType
145 static UINT DRIVE_GetDriveType( LPCWSTR name )
147 WCHAR buffer[20];
148 int i;
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 );
153 if(!buffer[0])
154 strcpyW(buffer,hdW);
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) );
161 return DRIVE_FIXED;
165 /***********************************************************************
166 * DRIVE_GetFSFlags
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 /***********************************************************************
181 * DRIVE_Init
183 int DRIVE_Init(void)
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];
189 WCHAR buffer[80];
190 struct stat drive_stat_buffer;
191 WCHAR *p;
192 DOSDRIVE *drive;
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 );
210 if (path[0])
212 /* Get the code page number */
213 PROFILE_GetWineIniString( name, CodepageW, zeroW, /* 0 == CP_ACP */
214 buffer, 80 );
215 drive->codepage = strtolW( buffer, NULL, 10 );
217 p = path + strlenW(path) - 1;
218 while ((p > path) && (*p == '/')) *p-- = '\0';
220 if (path[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);
226 else
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 );
242 drive->root = NULL;
243 continue;
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 );
250 drive->root = NULL;
251 continue;
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;
258 drive->flags = 0;
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 );
279 /* Get the device */
280 PROFILE_GetWineIniString( name, DeviceW, empty_strW, buffer, 80 );
281 if (buffer[0])
283 int cd_fd;
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);
296 close(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))
307 DRIVE_CurDrive = i;
309 count++;
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) );
319 if (!count)
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;
331 DRIVE_CurDrive = 2;
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))
341 DRIVE_CurDrive = i;
342 break;
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;
351 /* sanity check */
352 if (toupperW(path[0]) != drive_env[1] || path[1] != ':') continue;
353 DRIVE_Chdir( i, path + 2 );
355 return 1;
359 /***********************************************************************
360 * DRIVE_IsValid
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 );
390 return 0;
392 TRACE("%c:\n", 'A' + drive );
393 DRIVE_CurDrive = drive;
394 if (pTask) pTask->curdrive = drive | 0x80;
395 return 1;
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];
418 char *p;
419 struct stat st;
421 strcpy( buffer, *path );
422 while ((p = strchr( buffer, '\\' )) != NULL)
423 *p = '/';
424 len = strlen(buffer);
426 /* strip off trailing slashes */
427 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
429 for (;;)
431 /* Find the drive */
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))
438 continue;
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);
446 *path += len;
447 if (!**path) *path = "\\";
448 return drive;
452 if (len <= 1) return -1; /* reached root */
454 level = 0;
455 while (level < 1)
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;
463 buffer[len] = 0;
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];
480 LPCWSTR p = *path;
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))
493 continue;
494 if(len <= match_len) continue;
495 match_len = len;
496 rootdrive = drive;
497 p = *path + len;
500 if (rootdrive != -1)
502 *path = p;
503 TRACE("%s -> drive %c:, root='%s', name=%s\n",
504 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, debugstr_w(*path) );
506 return rootdrive;
510 /***********************************************************************
511 * DRIVE_GetRoot
513 const char * DRIVE_GetRoot( int drive )
515 if (!DRIVE_IsValid( drive )) return NULL;
516 return DOSDrives[drive].root;
520 /***********************************************************************
521 * DRIVE_GetDosCwd
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 /***********************************************************************
545 * DRIVE_GetUnixCwd
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 /***********************************************************************
569 * DRIVE_GetDevice
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;
585 char sig[3];
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 */
593 read(fd, &sig, 3);
594 if ((sig[0] == 'R') && (sig[1] == 'O') && (sig[2]=='M'))
596 extra_offs = 8;
598 lseek(fd, offs + extra_offs, SEEK_SET);
599 read(fd, &cur_vd_type, 1);
600 if (cur_vd_type == 0xff) /* voldesc set terminator */
601 break;
602 if (cur_vd_type > max_vd_type)
604 max_vd_type = cur_vd_type;
605 best_offs = offs + extra_offs;
608 return best_offs;
611 /***********************************************************************
612 * DRIVE_ReadSuperblock
614 * NOTE
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
621 int fd;
622 off_t offs;
623 int ret = 0;
625 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
626 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
628 struct stat st;
629 if (!DOSDrives[drive].device)
630 ERR("No device configured for drive %c: !\n", 'A'+drive);
631 else
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();
637 return -1;
640 switch(DOSDrives[drive].type)
642 case DRIVE_REMOVABLE:
643 case DRIVE_FIXED:
644 offs = 0;
645 break;
646 case DRIVE_CDROM:
647 offs = CDROM_Data_FindBestVoldesc(fd);
648 break;
649 default:
650 offs = 0;
651 break;
654 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs))
656 ret = -4;
657 goto the_end;
659 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER)
661 ret = -2;
662 goto the_end;
665 switch(DOSDrives[drive].type)
667 case DRIVE_REMOVABLE:
668 case DRIVE_FIXED:
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);
676 ret = -3;
677 goto the_end;
679 break;
680 case DRIVE_CDROM:
681 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
683 ret = -3;
684 goto the_end;
686 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
687 break;
688 default:
689 ret = -3;
690 goto the_end;
693 return close(fd);
694 the_end:
695 close(fd);
696 return ret;
700 /***********************************************************************
701 * DRIVE_WriteSuperblockEntry
703 * NOTE
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)
709 int fd;
711 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
713 ERR("Cannot open the device %s (for writing)\n",
714 DOSDrives[drive].device);
715 return -1;
717 if (lseek(fd,ofs,SEEK_SET)!=ofs)
719 ERR("lseek failed on device %s !\n",
720 DOSDrives[drive].device);
721 close(fd);
722 return -2;
724 if (write(fd,buff,len)!=len)
726 close(fd);
727 ERR("Cannot write on %s !\n",
728 DOSDrives[drive].device);
729 return -3;
731 return close (fd);
734 /******************************************************************
735 * static HANDLE CDROM_Open
739 static HANDLE CDROM_Open(int drive)
741 WCHAR root[] = {'\\','\\','.','\\','A',':',0};
742 root[4] += drive;
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;
757 if (offs)
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))
766 goto failure;
768 close(dev);
769 if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
770 && ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
771 { /* yippee, unicode */
772 int i;
773 WORD ch;
774 for (i=0; i<LABEL_LEN;i++)
775 { /* Motorola -> Intel Unicode conversion :-\ */
776 ch = label_read[i];
777 label_read[i] = (ch << 8) | (ch >> 8);
779 strncpyW(label, label_read, 11);
780 label[11] = 0;
782 else
784 MultiByteToWideChar(DOSDrives[drive].codepage, 0, (LPSTR)label_read, -1, label, 11);
785 label[11] = '\0';
787 return 1;
790 failure:
791 close(dev);
792 ERR("error reading label !\n");
793 return 0;
796 /**************************************************************************
797 * CDROM_GetLabel [internal]
799 static DWORD CDROM_GetLabel(int drive, WCHAR *label)
801 HANDLE h = CDROM_Open(drive);
802 CDROM_DISK_DATA cdd;
803 DWORD br;
804 DWORD ret = 1;
806 if (!h || !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
807 return 0;
809 switch (cdd.DiskData & 0x03)
811 case CDROM_DISK_DATA_TRACK:
812 if (!CDROM_Data_GetLabel(drive, label))
813 ret = 0;
814 break;
815 case CDROM_DISK_AUDIO_TRACK:
817 static const WCHAR audioCD[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
818 strcpyW(label, audioCD);
819 break;
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");
823 /* fall through */
824 case 0:
825 ret = 0;
826 break;
828 TRACE("CD: label is %s\n", debugstr_w(label));
830 return ret;
832 /***********************************************************************
833 * DRIVE_GetLabel
835 LPCWSTR DRIVE_GetLabel( int drive )
837 int read = 0;
838 char buff[DRIVE_SUPER];
839 int offs = -1;
841 if (!DRIVE_IsValid( drive )) return NULL;
842 if (DOSDrives[drive].type == DRIVE_CDROM)
844 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
846 else
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'));
852 else {
853 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
854 DOSDrives[drive].type == DRIVE_FIXED)
855 offs = 0x2b;
857 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
858 if (offs != -1)
859 MultiByteToWideChar(DOSDrives[drive].codepage, 0, buff+offs, 11,
860 DOSDrives[drive].label_read, 11);
861 DOSDrives[drive].label_read[11]='\0';
862 read = 1;
866 return (read) ?
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;
881 int i;
882 WORD wMagic;
883 DWORD dwStart, dwEnd, br;
884 CDROM_TOC toc;
886 if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
887 return 0;
890 * wMagic collects the wFrames from track 1
891 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
892 * frames.
893 * There it is collected for correcting the serial when there are less than
894 * 3 tracks.
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);
908 return serial;
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);
917 WORD offs;
918 union {
919 unsigned long val;
920 unsigned char p[4];
921 } serial;
922 BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
925 if (dev == -1) return 0;
926 offs = CDROM_Data_FindBestVoldesc(dev);
928 serial.val = 0;
929 if (offs)
931 BYTE buf[2048];
932 OSVERSIONINFOA ovi;
933 int i;
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);
943 GetVersionExA(&ovi);
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];
957 close(dev);
958 return serial.val;
961 /**************************************************************************
962 * CDROM_GetSerial [internal]
964 static DWORD CDROM_GetSerial(int drive)
966 DWORD serial = 0;
967 HANDLE h = CDROM_Open(drive);
968 CDROM_DISK_DATA cdd;
969 DWORD br;
971 if (!h || ! !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
972 return 0;
974 switch (cdd.DiskData & 0x03)
976 case CDROM_DISK_DATA_TRACK:
977 /* hopefully a data CD */
978 serial = CDROM_Data_GetSerial(drive);
979 break;
980 case CDROM_DISK_AUDIO_TRACK:
981 /* fall thru */
982 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
983 serial = CDROM_Audio_GetSerial(h);
984 break;
985 case 0:
986 break;
989 if (serial)
990 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
992 CloseHandle(h);
994 return serial;
997 /***********************************************************************
998 * DRIVE_GetSerialNumber
1000 DWORD DRIVE_GetSerialNumber( int drive )
1002 DWORD serial = 0;
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:
1014 case DRIVE_FIXED:
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);
1019 else
1020 serial = *((DWORD*)(buff+0x27));
1021 break;
1022 case DRIVE_CDROM:
1023 serial = CDROM_GetSerial(drive);
1024 break;
1025 default:
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;
1050 return 1;
1053 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
1054 DOSDrives[drive].serial_conf = serial;
1055 return 1;
1059 /***********************************************************************
1060 * DRIVE_GetType
1062 static UINT DRIVE_GetType( int drive )
1064 if (!DRIVE_IsValid( drive )) return DRIVE_NO_ROOT_DIR;
1065 return DOSDrives[drive].type;
1069 /***********************************************************************
1070 * DRIVE_GetFlags
1072 UINT DRIVE_GetFlags( int drive )
1074 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1075 return DOSDrives[drive].flags;
1078 /***********************************************************************
1079 * DRIVE_GetCodepage
1081 UINT DRIVE_GetCodepage( int drive )
1083 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1084 return DOSDrives[drive].codepage;
1088 /***********************************************************************
1089 * DRIVE_Chdir
1091 int DRIVE_Chdir( int drive, LPCWSTR path )
1093 DOS_FULL_NAME full_name;
1094 WCHAR buffer[MAX_PATHNAME_LEN];
1095 LPSTR unix_cwd;
1096 BY_HANDLE_FILE_INFORMATION info;
1097 TDB *pTask = TASK_GetCurrent();
1099 buffer[0] = 'A' + drive;
1100 buffer[1] = ':';
1101 buffer[2] = 0;
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 );
1111 return 0;
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();
1132 return 1;
1136 /***********************************************************************
1137 * DRIVE_Disable
1139 int DRIVE_Disable( int drive )
1141 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1143 SetLastError( ERROR_INVALID_DRIVE );
1144 return 0;
1146 DOSDrives[drive].flags |= DRIVE_DISABLED;
1147 return 1;
1151 /***********************************************************************
1152 * DRIVE_Enable
1154 int DRIVE_Enable( int drive )
1156 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1158 SetLastError( ERROR_INVALID_DRIVE );
1159 return 0;
1161 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1162 return 1;
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) ||
1180 !old->root ||
1181 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
1183 SetLastError( ERROR_INVALID_DRIVE );
1184 return 0;
1187 if ( new->root )
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))
1193 return 1;
1194 return 0;
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 );
1213 return 1;
1217 /***********************************************************************
1218 * DRIVE_OpenDevice
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 /***********************************************************************
1230 * DRIVE_RawRead
1232 * Read raw sectors from a device
1234 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1236 int fd;
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 );
1243 close( fd );
1245 else
1247 memset(dataptr, 0, nr_sect * 512);
1248 if (fake_success)
1250 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
1251 if (begin == 1) *dataptr = 0xf8;
1253 else
1254 return 0;
1256 return 1;
1260 /***********************************************************************
1261 * DRIVE_RawWrite
1263 * Write raw sectors to a device
1265 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1267 int fd;
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 );
1274 close( fd );
1276 else
1277 if (!(fake_success))
1278 return 0;
1280 return 1;
1284 /***********************************************************************
1285 * DRIVE_GetFreeSpace
1287 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
1288 PULARGE_INTEGER available )
1290 struct statfs info;
1292 if (!DRIVE_IsValid(drive))
1294 SetLastError( ERROR_PATH_NOT_FOUND );
1295 return 0;
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)
1301 #else
1302 if (statfs( DOSDrives[drive].root, &info) < 0)
1303 #endif
1305 FILE_SetDosError();
1306 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
1307 return 0;
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 );
1313 #else
1314 # ifdef STATFS_HAS_BFREE
1315 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
1316 # else
1317 # error "statfs has no bfree/bavail member!"
1318 # endif
1319 #endif
1320 if (DOSDrives[drive].type == DRIVE_CDROM)
1321 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1322 available->QuadPart = 0;
1324 return 1;
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 )
1336 UINT ret;
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 );
1346 return ret;
1350 /***********************************************************************
1351 * DRIVE_BuildEnv
1353 * Build the environment array containing the drives' current directories.
1354 * Resulting pointer must be freed with HeapFree.
1356 char *DRIVE_BuildEnv(void)
1358 int i, length = 0;
1359 LPCWSTR cwd[MAX_DOS_DRIVES];
1360 char *env, *p;
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);
1376 p += strlen(p) + 1;
1379 *p = 0;
1380 return env;
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
1410 * "E:\\" "E:" TRUE
1411 * "E:\\" "E" FALSE
1412 * "E:\\" "\\" TRUE
1413 * "E:\\TEST" "\\" TRUE
1414 * "E:\\TEST" ":\\" FALSE
1415 * "E:\\TEST" "E:\\" TRUE
1416 * "E:\\TEST" "" FALSE
1417 * "E:\\" "" FALSE (!)
1418 * "E:\\" 0x0 TRUE
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;
1429 LPCWSTR path;
1430 DWORD cluster_sec;
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();
1437 else
1438 if (root[0] && root[1] == ':') /* root contains drive tag */
1440 drive = toupperW(root[0]) - 'A';
1441 path = &root[2];
1442 if (path[0] == '\0')
1444 path = DRIVE_GetDosCwd(drive);
1445 if (!path)
1447 SetLastError(ERROR_PATH_NOT_FOUND);
1448 return FALSE;
1451 else
1452 if (path[0] == '\\')
1453 path++;
1455 if (path[0]) /* oops, we are in a subdir */
1457 SetLastError(ERROR_INVALID_NAME);
1458 return FALSE;
1461 else
1463 if (!root[0])
1464 SetLastError(ERROR_PATH_NOT_FOUND);
1465 else
1466 SetLastError(ERROR_INVALID_NAME);
1467 return FALSE;
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 */
1487 cluster_sec = 1;
1488 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1490 if (cluster_sectors)
1491 *cluster_sectors = cluster_sec;
1492 if (sector_bytes)
1493 *sector_bytes = sec_size;
1494 if (free_clusters)
1495 *free_clusters = available.s.LowPart / cluster_sec;
1496 if (total_clusters)
1497 *total_clusters = size.s.LowPart / cluster_sec;
1498 return TRUE;
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;
1510 BOOL ret = FALSE;
1512 if (root)
1514 if(!RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1516 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1517 return FALSE;
1520 else
1521 rootW.Buffer = NULL;
1523 ret = GetDiskFreeSpaceW(rootW.Buffer, cluster_sectors, sector_bytes,
1524 free_clusters, total_clusters );
1525 RtlFreeUnicodeString(&rootW);
1527 return ret;
1531 /***********************************************************************
1532 * GetDiskFreeSpaceExA (KERNEL32.@)
1534 * This function is used to acquire the size of the available and
1535 * total space on a logical volume.
1537 * RETURNS
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)
1548 int drive;
1549 ULARGE_INTEGER size,available;
1551 if (!root) drive = DRIVE_GetCurrentDrive();
1552 else
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 );
1560 return FALSE;
1562 drive = toupper(root[0]) - 'A';
1565 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1567 if (total)
1569 total->s.HighPart = size.s.HighPart;
1570 total->s.LowPart = size.s.LowPart;
1573 if (totalfree)
1575 totalfree->s.HighPart = available.s.HighPart;
1576 totalfree->s.LowPart = available.s.LowPart;
1579 if (avail)
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. */
1589 OSVERSIONINFOA ovi;
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;
1604 return TRUE;
1607 /***********************************************************************
1608 * GetDiskFreeSpaceExW (KERNEL32.@)
1610 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1611 PULARGE_INTEGER total,
1612 PULARGE_INTEGER totalfree)
1614 LPSTR xroot;
1615 BOOL ret;
1617 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1618 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1619 HeapFree( GetProcessHeap(), 0, xroot );
1620 return ret;
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.
1631 * RETURNS
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;
1639 return type;
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.
1649 * RETURNS
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 */
1663 int drive;
1664 TRACE("(%s)\n", debugstr_w(root));
1666 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1667 else
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;
1686 UINT ret = 0;
1688 if (root)
1690 if( !RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1692 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1693 return 0;
1696 else
1697 rootW.Buffer = NULL;
1699 ret = GetDriveTypeW(rootW.Buffer);
1701 RtlFreeUnicodeString(&rootW);
1702 return ret;
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 )
1724 UINT ret;
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 );
1731 return ret;
1733 GetLongPathNameW(shortname, longname, MAX_PATHNAME_LEN);
1734 ret = strlenW( longname ) + 1;
1735 if (ret > buflen) return ret;
1736 strcpyW(buf, longname);
1737 return ret - 1;
1740 /***********************************************************************
1741 * GetCurrentDirectoryA (KERNEL32.@)
1743 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1745 WCHAR bufferW[MAX_PATH];
1746 DWORD ret, retW;
1748 retW = GetCurrentDirectoryW(MAX_PATH, bufferW);
1750 if (!retW)
1751 ret = 0;
1752 else if (retW > MAX_PATH)
1754 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1755 ret = 0;
1757 else
1759 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1760 if (buflen >= ret)
1762 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, buflen, NULL, NULL);
1763 ret--; /* length without 0 */
1766 return ret;
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();
1786 if (!dir)
1788 SetLastError(ERROR_INVALID_PARAMETER);
1789 return FALSE;
1791 if (dir[0] && (dir[1]==':'))
1793 drive = toupperW( *dir ) - 'A';
1794 dir += 2;
1796 else
1797 drive = olddrive;
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 )))
1802 return FALSE;
1804 /* FIXME: what about empty strings? Add a \\ ? */
1805 if (!DRIVE_Chdir( drive, dir )) {
1806 DRIVE_SetCurrentDrive(olddrive);
1807 return FALSE;
1809 return TRUE;
1813 /***********************************************************************
1814 * SetCurrentDirectoryA (KERNEL32.@)
1816 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1818 UNICODE_STRING dirW;
1819 BOOL ret = FALSE;
1821 if (!dir)
1823 SetLastError(ERROR_INVALID_PARAMETER);
1824 return FALSE;
1827 if (RtlCreateUnicodeStringFromAsciiz(&dirW, dir))
1829 ret = SetCurrentDirectoryW(dirW.Buffer);
1830 RtlFreeUnicodeString(&dirW);
1832 else
1833 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1834 return ret;
1838 /***********************************************************************
1839 * GetLogicalDriveStringsA (KERNEL32.@)
1841 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1843 int drive, count;
1845 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1846 if (DRIVE_IsValid(drive)) count++;
1847 if ((count * 4) + 1 <= len)
1849 LPSTR p = buffer;
1850 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1851 if (DRIVE_IsValid(drive))
1853 *p++ = 'a' + drive;
1854 *p++ = ':';
1855 *p++ = '\\';
1856 *p++ = '\0';
1858 *p = '\0';
1859 return count * 4;
1861 else
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 )
1872 int drive, count;
1874 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1875 if (DRIVE_IsValid(drive)) count++;
1876 if (count * 4 * sizeof(WCHAR) <= len)
1878 LPWSTR p = buffer;
1879 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1880 if (DRIVE_IsValid(drive))
1882 *p++ = (WCHAR)('a' + drive);
1883 *p++ = (WCHAR)':';
1884 *p++ = (WCHAR)'\\';
1885 *p++ = (WCHAR)'\0';
1887 *p = (WCHAR)'\0';
1889 return count * 4 * sizeof(WCHAR);
1893 /***********************************************************************
1894 * GetLogicalDrives (KERNEL32.@)
1896 DWORD WINAPI GetLogicalDrives(void)
1898 DWORD ret = 0;
1899 int drive;
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);
1907 return ret;
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 )
1919 int drive;
1920 LPWSTR cp;
1922 /* FIXME, SetLastError()s missing */
1924 if (!root) drive = DRIVE_GetCurrentDrive();
1925 else
1927 if (root[0] && root[1] != ':')
1929 WARN("invalid root %s\n", debugstr_w(root));
1930 return FALSE;
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--;
1941 *cp = '\0';
1943 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1945 /* Set the filesystem information */
1946 /* Note: we only emulate a FAT fs at present */
1948 if (filename_len) {
1949 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1950 *filename_len = 12;
1951 else
1952 *filename_len = 255;
1954 if (flags)
1956 *flags=0;
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 );
1970 else
1972 static const WCHAR fatW[] = {'F','A','T',0};
1973 strncpyW( fsname, fatW, fsname_len );
1975 fsname[fsname_len - 1] = 0; /* ensure 0 termination */
1977 return TRUE;
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;
1991 BOOL ret;
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 );
2008 return ret;
2011 /***********************************************************************
2012 * SetVolumeLabelW (KERNEL32.@)
2014 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR volname )
2016 int drive;
2018 /* FIXME, SetLastErrors missing */
2020 if (!root) drive = DRIVE_GetCurrentDrive();
2021 else
2023 if ((root[1]) && (root[1] != ':'))
2025 WARN("invalid root %s\n", debugstr_w(root));
2026 return FALSE;
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 */
2037 return TRUE;
2040 /***********************************************************************
2041 * SetVolumeLabelA (KERNEL32.@)
2043 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
2045 UNICODE_STRING rootW, volnameW;
2046 BOOL ret;
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);
2057 return ret;