Define NONAMELESS{STRUCT,UNION} explicitly in the files that need them.
[wine/multimedia.git] / files / drive.c
blobc5c4b39330acef975070870293a290e1413c995a
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 "heap.h"
69 #include "msdos.h"
70 #include "task.h"
71 #include "wine/unicode.h"
72 #include "wine/library.h"
73 #include "wine/server.h"
74 #include "wine/debug.h"
76 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
77 WINE_DECLARE_DEBUG_CHANNEL(file);
79 typedef struct
81 char *root; /* root dir in Unix format without trailing / */
82 LPWSTR dos_cwd; /* cwd in DOS format without leading or trailing \ */
83 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
84 char *device; /* raw device path */
85 WCHAR label_conf[12]; /* drive label as cfg'd in wine config */
86 WCHAR label_read[12]; /* drive label as read from device */
87 DWORD serial_conf; /* drive serial number as cfg'd in wine config */
88 UINT type; /* drive type */
89 UINT flags; /* drive flags */
90 UINT codepage; /* drive code page */
91 dev_t dev; /* unix device number */
92 ino_t ino; /* unix inode number */
93 } DOSDRIVE;
96 static const WCHAR DRIVE_Types[][8] =
98 { 0 }, /* DRIVE_UNKNOWN */
99 { 0 }, /* DRIVE_NO_ROOT_DIR */
100 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
101 {'h','d',0}, /* DRIVE_FIXED */
102 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
103 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
104 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
108 /* Known filesystem types */
110 typedef struct
112 const WCHAR name[6];
113 UINT flags;
114 } FS_DESCR;
116 static const FS_DESCR DRIVE_Filesystems[] =
118 { {'u','n','i','x',0}, DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
119 { {'m','s','d','o','s',0}, DRIVE_SHORT_NAMES },
120 { {'d','o','s',0}, DRIVE_SHORT_NAMES },
121 { {'f','a','t',0}, DRIVE_SHORT_NAMES },
122 { {'v','f','a','t',0}, DRIVE_CASE_PRESERVING },
123 { {'w','i','n','9','5',0}, DRIVE_CASE_PRESERVING },
124 { { 0 }, 0 }
128 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
129 static int DRIVE_CurDrive = -1;
131 static HTASK16 DRIVE_LastTask = 0;
133 /* strdup on the process heap */
134 inline static char *heap_strdup( const char *str )
136 INT len = strlen(str) + 1;
137 LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
138 if (p) memcpy( p, str, len );
139 return p;
142 extern void CDROM_InitRegistry(int dev);
144 /***********************************************************************
145 * DRIVE_GetDriveType
147 static UINT DRIVE_GetDriveType( LPCWSTR name )
149 WCHAR buffer[20];
150 int i;
151 static const WCHAR TypeW[] = {'T','y','p','e',0};
152 static const WCHAR hdW[] = {'h','d',0};
154 PROFILE_GetWineIniString( name, TypeW, hdW, buffer, 20 );
155 if(!buffer[0])
156 strcpyW(buffer,hdW);
157 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
159 if (!strcmpiW( buffer, DRIVE_Types[i] )) return i;
161 MESSAGE("%s: unknown drive type %s, defaulting to 'hd'.\n",
162 debugstr_w(name), debugstr_w(buffer) );
163 return DRIVE_FIXED;
167 /***********************************************************************
168 * DRIVE_GetFSFlags
170 static UINT DRIVE_GetFSFlags( LPCWSTR name, LPCWSTR value )
172 const FS_DESCR *descr;
174 for (descr = DRIVE_Filesystems; *descr->name; descr++)
175 if (!strcmpiW( value, descr->name )) return descr->flags;
176 MESSAGE("%s: unknown filesystem type %s, defaulting to 'win95'.\n",
177 debugstr_w(name), debugstr_w(value) );
178 return DRIVE_CASE_PRESERVING;
182 /***********************************************************************
183 * DRIVE_Init
185 int DRIVE_Init(void)
187 int i, len, count = 0;
188 WCHAR name[] = {'D','r','i','v','e',' ','A',0};
189 WCHAR drive_env[] = {'=','A',':',0};
190 WCHAR path[MAX_PATHNAME_LEN];
191 WCHAR buffer[80];
192 struct stat drive_stat_buffer;
193 WCHAR *p;
194 DOSDRIVE *drive;
195 static const WCHAR PathW[] = {'P','a','t','h',0};
196 static const WCHAR empty_strW[] = { 0 };
197 static const WCHAR CodepageW[] = {'C','o','d','e','p','a','g','e',0};
198 static const WCHAR LabelW[] = {'L','a','b','e','l',0};
199 static const WCHAR SerialW[] = {'S','e','r','i','a','l',0};
200 static const WCHAR zeroW[] = {'0',0};
201 static const WCHAR def_serialW[] = {'1','2','3','4','5','6','7','8',0};
202 static const WCHAR FilesystemW[] = {'F','i','l','e','s','y','s','t','e','m',0};
203 static const WCHAR win95W[] = {'w','i','n','9','5',0};
204 static const WCHAR DeviceW[] = {'D','e','v','i','c','e',0};
205 static const WCHAR ReadVolInfoW[] = {'R','e','a','d','V','o','l','I','n','f','o',0};
206 static const WCHAR FailReadOnlyW[] = {'F','a','i','l','R','e','a','d','O','n','l','y',0};
207 static const WCHAR driveC_labelW[] = {'D','r','i','v','e',' ','C',' ',' ',' ',' ',0};
209 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
211 PROFILE_GetWineIniString( name, PathW, empty_strW, path, MAX_PATHNAME_LEN );
212 if (path[0])
214 /* Get the code page number */
215 PROFILE_GetWineIniString( name, CodepageW, zeroW, /* 0 == CP_ACP */
216 buffer, 80 );
217 drive->codepage = strtolW( buffer, NULL, 10 );
219 p = path + strlenW(path) - 1;
220 while ((p > path) && (*p == '/')) *p-- = '\0';
222 if (path[0] == '/')
224 len = WideCharToMultiByte(drive->codepage, 0, path, -1, NULL, 0, NULL, NULL);
225 drive->root = HeapAlloc(GetProcessHeap(), 0, len);
226 WideCharToMultiByte(drive->codepage, 0, path, -1, drive->root, len, NULL, NULL);
228 else
230 /* relative paths are relative to config dir */
231 const char *config = wine_get_config_dir();
232 len = strlen(config);
233 len += WideCharToMultiByte(drive->codepage, 0, path, -1, NULL, 0, NULL, NULL) + 2;
234 drive->root = HeapAlloc( GetProcessHeap(), 0, len );
235 len -= sprintf( drive->root, "%s/", config );
236 WideCharToMultiByte(drive->codepage, 0, path, -1, drive->root + strlen(drive->root), len, NULL, NULL);
239 if (stat( drive->root, &drive_stat_buffer ))
241 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
242 drive->root, strerror(errno), 'A' + i);
243 HeapFree( GetProcessHeap(), 0, drive->root );
244 drive->root = NULL;
245 continue;
247 if (!S_ISDIR(drive_stat_buffer.st_mode))
249 MESSAGE("%s is not a directory, ignoring drive %c:\n",
250 drive->root, 'A' + i );
251 HeapFree( GetProcessHeap(), 0, drive->root );
252 drive->root = NULL;
253 continue;
256 drive->dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(drive->dos_cwd[0]));
257 drive->unix_cwd = heap_strdup( "" );
258 drive->type = DRIVE_GetDriveType( name );
259 drive->device = NULL;
260 drive->flags = 0;
261 drive->dev = drive_stat_buffer.st_dev;
262 drive->ino = drive_stat_buffer.st_ino;
264 /* Get the drive label */
265 PROFILE_GetWineIniString( name, LabelW, empty_strW, drive->label_conf, 12 );
266 if ((len = strlenW(drive->label_conf)) < 11)
268 /* Pad label with spaces */
269 while(len < 11) drive->label_conf[len++] = ' ';
270 drive->label_conf[11] = '\0';
273 /* Get the serial number */
274 PROFILE_GetWineIniString( name, SerialW, def_serialW, buffer, 80 );
275 drive->serial_conf = strtoulW( buffer, NULL, 16 );
277 /* Get the filesystem type */
278 PROFILE_GetWineIniString( name, FilesystemW, win95W, buffer, 80 );
279 drive->flags = DRIVE_GetFSFlags( name, buffer );
281 /* Get the device */
282 PROFILE_GetWineIniString( name, DeviceW, empty_strW, buffer, 80 );
283 if (buffer[0])
285 int cd_fd;
286 len = WideCharToMultiByte(CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL);
287 drive->device = HeapAlloc(GetProcessHeap(), 0, len);
288 WideCharToMultiByte(drive->codepage, 0, buffer, -1, drive->device, len, NULL, NULL);
290 if (PROFILE_GetWineIniBool( name, ReadVolInfoW, 1))
291 drive->flags |= DRIVE_READ_VOL_INFO;
293 if (drive->type == DRIVE_CDROM)
295 if ((cd_fd = open(drive->device, O_RDONLY|O_NONBLOCK)) != -1)
297 CDROM_InitRegistry(cd_fd);
298 close(cd_fd);
303 /* Get the FailReadOnly flag */
304 if (PROFILE_GetWineIniBool( name, FailReadOnlyW, 0 ))
305 drive->flags |= DRIVE_FAIL_READ_ONLY;
307 /* Make the first hard disk the current drive */
308 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
309 DRIVE_CurDrive = i;
311 count++;
312 TRACE("%s: path=%s type=%s label=%s serial=%08lx "
313 "flags=%08x codepage=%u dev=%x ino=%x\n",
314 debugstr_w(name), drive->root, debugstr_w(DRIVE_Types[drive->type]),
315 debugstr_w(drive->label_conf), drive->serial_conf, drive->flags,
316 drive->codepage, (int)drive->dev, (int)drive->ino );
318 else WARN("%s: not defined\n", debugstr_w(name) );
321 if (!count)
323 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
324 /* Create a C drive pointing to Unix root dir */
325 DOSDrives[2].root = heap_strdup( "/" );
326 DOSDrives[2].dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DOSDrives[2].dos_cwd[0]));
327 DOSDrives[2].unix_cwd = heap_strdup( "" );
328 strcpyW( DOSDrives[2].label_conf, driveC_labelW );
329 DOSDrives[2].serial_conf = 12345678;
330 DOSDrives[2].type = DRIVE_FIXED;
331 DOSDrives[2].device = NULL;
332 DOSDrives[2].flags = 0;
333 DRIVE_CurDrive = 2;
336 /* Make sure the current drive is valid */
337 if (DRIVE_CurDrive == -1)
339 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
341 if (drive->root && !(drive->flags & DRIVE_DISABLED))
343 DRIVE_CurDrive = i;
344 break;
349 /* get current working directory info for all drives */
350 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
352 if (!GetEnvironmentVariableW(drive_env, path, MAX_PATHNAME_LEN)) continue;
353 /* sanity check */
354 if (toupperW(path[0]) != drive_env[1] || path[1] != ':') continue;
355 DRIVE_Chdir( i, path + 2 );
357 return 1;
361 /***********************************************************************
362 * DRIVE_IsValid
364 int DRIVE_IsValid( int drive )
366 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
367 return (DOSDrives[drive].root &&
368 !(DOSDrives[drive].flags & DRIVE_DISABLED));
372 /***********************************************************************
373 * DRIVE_GetCurrentDrive
375 int DRIVE_GetCurrentDrive(void)
377 TDB *pTask = TASK_GetCurrent();
378 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
379 return DRIVE_CurDrive;
383 /***********************************************************************
384 * DRIVE_SetCurrentDrive
386 int DRIVE_SetCurrentDrive( int drive )
388 TDB *pTask = TASK_GetCurrent();
389 if (!DRIVE_IsValid( drive ))
391 SetLastError( ERROR_INVALID_DRIVE );
392 return 0;
394 TRACE("%c:\n", 'A' + drive );
395 DRIVE_CurDrive = drive;
396 if (pTask) pTask->curdrive = drive | 0x80;
397 return 1;
401 /***********************************************************************
402 * DRIVE_FindDriveRoot
404 * Find a drive for which the root matches the beginning of the given path.
405 * This can be used to translate a Unix path into a drive + DOS path.
406 * Return value is the drive, or -1 on error. On success, path is modified
407 * to point to the beginning of the DOS path.
409 * Note: path must be in the encoding of the underlying Unix file system.
411 int DRIVE_FindDriveRoot( const char **path )
413 /* Starting with the full path, check if the device and inode match any of
414 * the wine 'drives'. If not then remove the last path component and try
415 * again. If the last component was a '..' then skip a normal component
416 * since it's a directory that's ascended back out of.
418 int drive, level, len;
419 char buffer[MAX_PATHNAME_LEN];
420 char *p;
421 struct stat st;
423 strcpy( buffer, *path );
424 while ((p = strchr( buffer, '\\' )) != NULL)
425 *p = '/';
426 len = strlen(buffer);
428 /* strip off trailing slashes */
429 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
431 for (;;)
433 /* Find the drive */
434 if (stat( buffer, &st ) == 0 && S_ISDIR( st.st_mode ))
436 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
438 if (!DOSDrives[drive].root ||
439 (DOSDrives[drive].flags & DRIVE_DISABLED))
440 continue;
442 if ((DOSDrives[drive].dev == st.st_dev) &&
443 (DOSDrives[drive].ino == st.st_ino))
445 if (len == 1) len = 0; /* preserve root slash in returned path */
446 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
447 *path, 'A' + drive, buffer, *path + len);
448 *path += len;
449 if (!**path) *path = "\\";
450 return drive;
454 if (len <= 1) return -1; /* reached root */
456 level = 0;
457 while (level < 1)
459 /* find start of the last path component */
460 while (len > 1 && buffer[len - 1] != '/') len--;
461 if (!buffer[len]) break; /* empty component -> reached root */
462 /* does removing it take us up a level? */
463 if (strcmp( buffer + len, "." ) != 0)
464 level += strcmp( buffer + len, ".." ) ? 1 : -1;
465 buffer[len] = 0;
466 /* strip off trailing slashes */
467 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
473 /***********************************************************************
474 * DRIVE_FindDriveRootW
476 * Unicode version of DRIVE_FindDriveRoot.
478 int DRIVE_FindDriveRootW( LPCWSTR *path )
480 int drive, rootdrive = -1;
481 char buffer[MAX_PATHNAME_LEN];
482 LPCWSTR p = *path;
483 int len, match_len = -1;
485 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
487 if (!DOSDrives[drive].root ||
488 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
490 WideCharToMultiByte(DOSDrives[drive].codepage, 0, *path, -1,
491 buffer, MAX_PATHNAME_LEN, NULL, NULL);
493 len = strlen(DOSDrives[drive].root);
494 if(strncmp(DOSDrives[drive].root, buffer, len))
495 continue;
496 if(len <= match_len) continue;
497 match_len = len;
498 rootdrive = drive;
499 p = *path + len;
502 if (rootdrive != -1)
504 *path = p;
505 TRACE("%s -> drive %c:, root='%s', name=%s\n",
506 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, debugstr_w(*path) );
508 return rootdrive;
512 /***********************************************************************
513 * DRIVE_GetRoot
515 const char * DRIVE_GetRoot( int drive )
517 if (!DRIVE_IsValid( drive )) return NULL;
518 return DOSDrives[drive].root;
522 /***********************************************************************
523 * DRIVE_GetDosCwd
525 LPCWSTR DRIVE_GetDosCwd( int drive )
527 TDB *pTask = TASK_GetCurrent();
528 if (!DRIVE_IsValid( drive )) return NULL;
530 /* Check if we need to change the directory to the new task. */
531 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
532 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
533 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
535 static const WCHAR rootW[] = {'\\',0};
536 WCHAR curdirW[MAX_PATH];
537 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
538 /* Perform the task-switch */
539 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
540 DRIVE_LastTask = GetCurrentTask();
542 return DOSDrives[drive].dos_cwd;
546 /***********************************************************************
547 * DRIVE_GetUnixCwd
549 const char * DRIVE_GetUnixCwd( int drive )
551 TDB *pTask = TASK_GetCurrent();
552 if (!DRIVE_IsValid( drive )) return NULL;
554 /* Check if we need to change the directory to the new task. */
555 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
556 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
557 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
559 static const WCHAR rootW[] = {'\\',0};
560 WCHAR curdirW[MAX_PATH];
561 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
562 /* Perform the task-switch */
563 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
564 DRIVE_LastTask = GetCurrentTask();
566 return DOSDrives[drive].unix_cwd;
570 /***********************************************************************
571 * DRIVE_GetDevice
573 const char * DRIVE_GetDevice( int drive )
575 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
578 /******************************************************************
579 * static WORD CDROM_Data_FindBestVoldesc
583 static WORD CDROM_Data_FindBestVoldesc(int fd)
585 BYTE cur_vd_type, max_vd_type = 0;
586 unsigned int offs, best_offs = 0, extra_offs = 0;
587 char sig[3];
589 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
591 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
592 * the volume label is displaced forward by 8
594 lseek(fd, offs + 11, SEEK_SET); /* check for non-ISO9660 signature */
595 read(fd, &sig, 3);
596 if ((sig[0] == 'R') && (sig[1] == 'O') && (sig[2]=='M'))
598 extra_offs = 8;
600 lseek(fd, offs + extra_offs, SEEK_SET);
601 read(fd, &cur_vd_type, 1);
602 if (cur_vd_type == 0xff) /* voldesc set terminator */
603 break;
604 if (cur_vd_type > max_vd_type)
606 max_vd_type = cur_vd_type;
607 best_offs = offs + extra_offs;
610 return best_offs;
613 /***********************************************************************
614 * DRIVE_ReadSuperblock
616 * NOTE
617 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
618 * to check, that they are writing on a FAT filesystem !
620 int DRIVE_ReadSuperblock (int drive, char * buff)
622 #define DRIVE_SUPER 96
623 int fd;
624 off_t offs;
625 int ret = 0;
627 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
628 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
630 struct stat st;
631 if (!DOSDrives[drive].device)
632 ERR("No device configured for drive %c: !\n", 'A'+drive);
633 else
634 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
635 (stat(DOSDrives[drive].device, &st)) ?
636 "not available or symlink not valid ?" : "no permission");
637 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
638 PROFILE_UsageWineIni();
639 return -1;
642 switch(DOSDrives[drive].type)
644 case DRIVE_REMOVABLE:
645 case DRIVE_FIXED:
646 offs = 0;
647 break;
648 case DRIVE_CDROM:
649 offs = CDROM_Data_FindBestVoldesc(fd);
650 break;
651 default:
652 offs = 0;
653 break;
656 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs))
658 ret = -4;
659 goto the_end;
661 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER)
663 ret = -2;
664 goto the_end;
667 switch(DOSDrives[drive].type)
669 case DRIVE_REMOVABLE:
670 case DRIVE_FIXED:
671 if ((buff[0x26]!=0x29) || /* Check for FAT present */
672 /* FIXME: do really all FAT have their name beginning with
673 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
674 memcmp( buff+0x36,"FAT",3))
676 ERR("The filesystem is not FAT !! (device=%s)\n",
677 DOSDrives[drive].device);
678 ret = -3;
679 goto the_end;
681 break;
682 case DRIVE_CDROM:
683 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
685 ret = -3;
686 goto the_end;
688 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
689 break;
690 default:
691 ret = -3;
692 goto the_end;
695 return close(fd);
696 the_end:
697 close(fd);
698 return ret;
702 /***********************************************************************
703 * DRIVE_WriteSuperblockEntry
705 * NOTE
706 * We are writing as little as possible (ie. not the whole SuperBlock)
707 * not to interfere with kernel. The drive can be mounted !
709 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
711 int fd;
713 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
715 ERR("Cannot open the device %s (for writing)\n",
716 DOSDrives[drive].device);
717 return -1;
719 if (lseek(fd,ofs,SEEK_SET)!=ofs)
721 ERR("lseek failed on device %s !\n",
722 DOSDrives[drive].device);
723 close(fd);
724 return -2;
726 if (write(fd,buff,len)!=len)
728 close(fd);
729 ERR("Cannot write on %s !\n",
730 DOSDrives[drive].device);
731 return -3;
733 return close (fd);
736 /******************************************************************
737 * static HANDLE CDROM_Open
741 static HANDLE CDROM_Open(int drive)
743 WCHAR root[] = {'\\','\\','.','\\','A',':',0};
744 root[4] += drive;
745 return CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
748 /**************************************************************************
749 * CDROM_Data_GetLabel [internal]
751 DWORD CDROM_Data_GetLabel(int drive, WCHAR *label)
753 #define LABEL_LEN 32+1
754 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
755 WORD offs = CDROM_Data_FindBestVoldesc(dev);
756 WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
757 DWORD unicode_id = 0;
759 if (offs)
761 if ((lseek(dev, offs+0x58, SEEK_SET) == offs+0x58)
762 && (read(dev, &unicode_id, 3) == 3))
764 int ver = (unicode_id & 0xff0000) >> 16;
766 if ((lseek(dev, offs+0x28, SEEK_SET) != offs+0x28)
767 || (read(dev, &label_read, LABEL_LEN) != LABEL_LEN))
768 goto failure;
770 close(dev);
771 if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
772 && ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
773 { /* yippee, unicode */
774 int i;
775 WORD ch;
776 for (i=0; i<LABEL_LEN;i++)
777 { /* Motorola -> Intel Unicode conversion :-\ */
778 ch = label_read[i];
779 label_read[i] = (ch << 8) | (ch >> 8);
781 strncpyW(label, label_read, 11);
782 label[11] = 0;
784 else
786 MultiByteToWideChar(DOSDrives[drive].codepage, 0, (LPSTR)label_read, -1, label, 11);
787 label[11] = '\0';
789 return 1;
792 failure:
793 close(dev);
794 ERR("error reading label !\n");
795 return 0;
798 /**************************************************************************
799 * CDROM_GetLabel [internal]
801 static DWORD CDROM_GetLabel(int drive, WCHAR *label)
803 HANDLE h = CDROM_Open(drive);
804 CDROM_DISK_DATA cdd;
805 DWORD br;
806 DWORD ret = 1;
808 if (!h || !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
809 return 0;
811 switch (cdd.DiskData & 0x03)
813 case CDROM_DISK_DATA_TRACK:
814 if (!CDROM_Data_GetLabel(drive, label))
815 ret = 0;
816 break;
817 case CDROM_DISK_AUDIO_TRACK:
819 static const WCHAR audioCD[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
820 strcpyW(label, audioCD);
821 break;
823 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
824 FIXME("Need to get the label of a mixed mode CD: not implemented yet !\n");
825 /* fall through */
826 case 0:
827 ret = 0;
828 break;
830 TRACE("CD: label is %s\n", debugstr_w(label));
832 return ret;
834 /***********************************************************************
835 * DRIVE_GetLabel
837 LPCWSTR DRIVE_GetLabel( int drive )
839 int read = 0;
840 char buff[DRIVE_SUPER];
841 int offs = -1;
843 if (!DRIVE_IsValid( drive )) return NULL;
844 if (DOSDrives[drive].type == DRIVE_CDROM)
846 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
848 else
849 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
851 if (DRIVE_ReadSuperblock(drive,(char *) buff))
852 ERR("Invalid or unreadable superblock on %s (%c:).\n",
853 DOSDrives[drive].device, (char)(drive+'A'));
854 else {
855 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
856 DOSDrives[drive].type == DRIVE_FIXED)
857 offs = 0x2b;
859 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
860 if (offs != -1)
861 MultiByteToWideChar(DOSDrives[drive].codepage, 0, buff+offs, 11,
862 DOSDrives[drive].label_read, 11);
863 DOSDrives[drive].label_read[11]='\0';
864 read = 1;
868 return (read) ?
869 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
872 #define CDFRAMES_PERSEC 75
873 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
874 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
875 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
877 /**************************************************************************
878 * CDROM_Audio_GetSerial [internal]
880 static DWORD CDROM_Audio_GetSerial(HANDLE h)
882 unsigned long serial = 0;
883 int i;
884 WORD wMagic;
885 DWORD dwStart, dwEnd, br;
886 CDROM_TOC toc;
888 if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
889 return 0;
892 * wMagic collects the wFrames from track 1
893 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
894 * frames.
895 * There it is collected for correcting the serial when there are less than
896 * 3 tracks.
898 wMagic = toc.TrackData[0].Address[2];
899 dwStart = FRAME_OF_TOC(toc, toc.FirstTrack);
901 for (i = 0; i <= toc.LastTrack - toc.FirstTrack; i++) {
902 serial += (toc.TrackData[i].Address[0] << 16) |
903 (toc.TrackData[i].Address[1] << 8) | toc.TrackData[i].Address[2];
905 dwEnd = FRAME_OF_TOC(toc, toc.LastTrack + 1);
907 if (toc.LastTrack - toc.FirstTrack + 1 < 3)
908 serial += wMagic + (dwEnd - dwStart);
910 return serial;
913 /**************************************************************************
914 * CDROM_Data_GetSerial [internal]
916 static DWORD CDROM_Data_GetSerial(int drive)
918 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
919 WORD offs;
920 union {
921 unsigned long val;
922 unsigned char p[4];
923 } serial;
924 BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
927 if (dev == -1) return 0;
928 offs = CDROM_Data_FindBestVoldesc(dev);
930 serial.val = 0;
931 if (offs)
933 BYTE buf[2048];
934 OSVERSIONINFOA ovi;
935 int i;
937 lseek(dev, offs, SEEK_SET);
938 read(dev, buf, 2048);
940 * OK, another braindead one... argh. Just believe it.
941 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
942 * It's true and nobody will ever be able to change it.
944 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
945 GetVersionExA(&ovi);
946 if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (ovi.dwMajorVersion >= 4))
948 b0 = 3; b1 = 2; b2 = 1; b3 = 0;
950 for (i = 0; i < 2048; i += 4)
952 /* DON'T optimize this into DWORD !! (breaks overflow) */
953 serial.p[b0] += buf[i+b0];
954 serial.p[b1] += buf[i+b1];
955 serial.p[b2] += buf[i+b2];
956 serial.p[b3] += buf[i+b3];
959 close(dev);
960 return serial.val;
963 /**************************************************************************
964 * CDROM_GetSerial [internal]
966 static DWORD CDROM_GetSerial(int drive)
968 DWORD serial = 0;
969 HANDLE h = CDROM_Open(drive);
970 CDROM_DISK_DATA cdd;
971 DWORD br;
973 if (!h || ! !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
974 return 0;
976 switch (cdd.DiskData & 0x03)
978 case CDROM_DISK_DATA_TRACK:
979 /* hopefully a data CD */
980 serial = CDROM_Data_GetSerial(drive);
981 break;
982 case CDROM_DISK_AUDIO_TRACK:
983 /* fall thru */
984 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
985 serial = CDROM_Audio_GetSerial(h);
986 break;
987 case 0:
988 break;
991 if (serial)
992 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
994 CloseHandle(h);
996 return serial;
999 /***********************************************************************
1000 * DRIVE_GetSerialNumber
1002 DWORD DRIVE_GetSerialNumber( int drive )
1004 DWORD serial = 0;
1005 char buff[DRIVE_SUPER];
1007 TRACE("drive %d, type = %d\n", drive, DOSDrives[drive].type);
1009 if (!DRIVE_IsValid( drive )) return 0;
1011 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1013 switch(DOSDrives[drive].type)
1015 case DRIVE_REMOVABLE:
1016 case DRIVE_FIXED:
1017 if (DRIVE_ReadSuperblock(drive,(char *) buff))
1018 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
1019 " Maybe not FAT?\n" ,
1020 DOSDrives[drive].device, 'A'+drive);
1021 else
1022 serial = *((DWORD*)(buff+0x27));
1023 break;
1024 case DRIVE_CDROM:
1025 serial = CDROM_GetSerial(drive);
1026 break;
1027 default:
1028 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
1032 return (serial) ? serial : DOSDrives[drive].serial_conf;
1036 /***********************************************************************
1037 * DRIVE_SetSerialNumber
1039 int DRIVE_SetSerialNumber( int drive, DWORD serial )
1041 char buff[DRIVE_SUPER];
1043 if (!DRIVE_IsValid( drive )) return 0;
1045 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1047 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
1048 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
1049 /* check, if the drive has a FAT filesystem */
1050 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
1051 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
1052 return 1;
1055 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
1056 DOSDrives[drive].serial_conf = serial;
1057 return 1;
1061 /***********************************************************************
1062 * DRIVE_GetType
1064 static UINT DRIVE_GetType( int drive )
1066 if (!DRIVE_IsValid( drive )) return DRIVE_NO_ROOT_DIR;
1067 return DOSDrives[drive].type;
1071 /***********************************************************************
1072 * DRIVE_GetFlags
1074 UINT DRIVE_GetFlags( int drive )
1076 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1077 return DOSDrives[drive].flags;
1080 /***********************************************************************
1081 * DRIVE_GetCodepage
1083 UINT DRIVE_GetCodepage( int drive )
1085 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1086 return DOSDrives[drive].codepage;
1090 /***********************************************************************
1091 * DRIVE_Chdir
1093 int DRIVE_Chdir( int drive, LPCWSTR path )
1095 DOS_FULL_NAME full_name;
1096 WCHAR buffer[MAX_PATHNAME_LEN];
1097 LPSTR unix_cwd;
1098 BY_HANDLE_FILE_INFORMATION info;
1099 TDB *pTask = TASK_GetCurrent();
1101 buffer[0] = 'A' + drive;
1102 buffer[1] = ':';
1103 buffer[2] = 0;
1104 TRACE("(%s,%s)\n", debugstr_w(buffer), debugstr_w(path) );
1105 strncpyW( buffer + 2, path, MAX_PATHNAME_LEN - 2 );
1106 buffer[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1108 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
1109 if (!FILE_Stat( full_name.long_name, &info, NULL )) return 0;
1110 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1112 SetLastError( ERROR_FILE_NOT_FOUND );
1113 return 0;
1115 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
1116 while (*unix_cwd == '/') unix_cwd++;
1118 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1119 'A' + drive, unix_cwd, debugstr_w(full_name.short_name + 3) );
1121 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
1122 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
1123 DOSDrives[drive].dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name.short_name) - 2) * sizeof(WCHAR));
1124 strcpyW(DOSDrives[drive].dos_cwd, full_name.short_name + 3);
1125 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
1127 if (pTask && (pTask->curdrive & 0x80) &&
1128 ((pTask->curdrive & ~0x80) == drive))
1130 WideCharToMultiByte(CP_ACP, 0, full_name.short_name + 2, -1,
1131 pTask->curdir, sizeof(pTask->curdir), NULL, NULL);
1132 DRIVE_LastTask = GetCurrentTask();
1134 return 1;
1138 /***********************************************************************
1139 * DRIVE_Disable
1141 int DRIVE_Disable( int drive )
1143 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1145 SetLastError( ERROR_INVALID_DRIVE );
1146 return 0;
1148 DOSDrives[drive].flags |= DRIVE_DISABLED;
1149 return 1;
1153 /***********************************************************************
1154 * DRIVE_Enable
1156 int DRIVE_Enable( int drive )
1158 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1160 SetLastError( ERROR_INVALID_DRIVE );
1161 return 0;
1163 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1164 return 1;
1168 /***********************************************************************
1169 * DRIVE_SetLogicalMapping
1171 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
1173 /* If new_drive is already valid, do nothing and return 0
1174 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1176 DOSDRIVE *old, *new;
1178 old = DOSDrives + existing_drive;
1179 new = DOSDrives + new_drive;
1181 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
1182 !old->root ||
1183 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
1185 SetLastError( ERROR_INVALID_DRIVE );
1186 return 0;
1189 if ( new->root )
1191 TRACE("Can't map drive %c: to already existing drive %c:\n",
1192 'A' + existing_drive, 'A' + new_drive );
1193 /* it is already mapped there, so return success */
1194 if (!strcmp(old->root,new->root))
1195 return 1;
1196 return 0;
1199 new->root = heap_strdup( old->root );
1200 new->dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(old->dos_cwd) + 1) * sizeof(WCHAR));
1201 strcpyW(new->dos_cwd, old->dos_cwd);
1202 new->unix_cwd = heap_strdup( old->unix_cwd );
1203 new->device = heap_strdup( old->device );
1204 memcpy ( new->label_conf, old->label_conf, 12 );
1205 memcpy ( new->label_read, old->label_read, 12 );
1206 new->serial_conf = old->serial_conf;
1207 new->type = old->type;
1208 new->flags = old->flags;
1209 new->dev = old->dev;
1210 new->ino = old->ino;
1212 TRACE("Drive %c: is now equal to drive %c:\n",
1213 'A' + new_drive, 'A' + existing_drive );
1215 return 1;
1219 /***********************************************************************
1220 * DRIVE_OpenDevice
1222 * Open the drive raw device and return a Unix fd (or -1 on error).
1224 int DRIVE_OpenDevice( int drive, int flags )
1226 if (!DRIVE_IsValid( drive )) return -1;
1227 return open( DOSDrives[drive].device, flags );
1231 /***********************************************************************
1232 * DRIVE_RawRead
1234 * Read raw sectors from a device
1236 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1238 int fd;
1240 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1242 lseek( fd, begin * 512, SEEK_SET );
1243 /* FIXME: check errors */
1244 read( fd, dataptr, nr_sect * 512 );
1245 close( fd );
1247 else
1249 memset(dataptr, 0, nr_sect * 512);
1250 if (fake_success)
1252 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
1253 if (begin == 1) *dataptr = 0xf8;
1255 else
1256 return 0;
1258 return 1;
1262 /***********************************************************************
1263 * DRIVE_RawWrite
1265 * Write raw sectors to a device
1267 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1269 int fd;
1271 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1273 lseek( fd, begin * 512, SEEK_SET );
1274 /* FIXME: check errors */
1275 write( fd, dataptr, nr_sect * 512 );
1276 close( fd );
1278 else
1279 if (!(fake_success))
1280 return 0;
1282 return 1;
1286 /***********************************************************************
1287 * DRIVE_GetFreeSpace
1289 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
1290 PULARGE_INTEGER available )
1292 struct statfs info;
1294 if (!DRIVE_IsValid(drive))
1296 SetLastError( ERROR_PATH_NOT_FOUND );
1297 return 0;
1300 /* FIXME: add autoconf check for this */
1301 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1302 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
1303 #else
1304 if (statfs( DOSDrives[drive].root, &info) < 0)
1305 #endif
1307 FILE_SetDosError();
1308 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
1309 return 0;
1312 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
1313 #ifdef STATFS_HAS_BAVAIL
1314 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
1315 #else
1316 # ifdef STATFS_HAS_BFREE
1317 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
1318 # else
1319 # error "statfs has no bfree/bavail member!"
1320 # endif
1321 #endif
1322 if (DOSDrives[drive].type == DRIVE_CDROM)
1323 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1324 available->QuadPart = 0;
1326 return 1;
1329 /***********************************************************************
1330 * DRIVE_GetCurrentDirectory
1331 * Returns "X:\\path\\etc\\".
1333 * Despite the API description, return required length including the
1334 * terminating null when buffer too small. This is the real behaviour.
1336 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPWSTR buf )
1338 UINT ret;
1339 LPCWSTR dos_cwd = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1340 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1342 ret = strlenW(dos_cwd) + 3; /* length of WHOLE current directory */
1343 if (ret >= buflen) return ret + 1;
1345 strcpyW( buf, driveA_rootW );
1346 buf[0] += DRIVE_GetCurrentDrive();
1347 strcatW( buf, dos_cwd );
1348 return ret;
1352 /***********************************************************************
1353 * DRIVE_BuildEnv
1355 * Build the environment array containing the drives' current directories.
1356 * Resulting pointer must be freed with HeapFree.
1358 char *DRIVE_BuildEnv(void)
1360 int i, length = 0;
1361 LPCWSTR cwd[MAX_DOS_DRIVES];
1362 char *env, *p;
1364 for (i = 0; i < MAX_DOS_DRIVES; i++)
1366 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0])
1367 length += WideCharToMultiByte(DRIVE_GetCodepage(i), 0,
1368 cwd[i], -1, NULL, 0, NULL, NULL) + 7;
1370 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
1371 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
1373 if (cwd[i] && cwd[i][0])
1375 *p++ = '='; *p++ = 'A' + i; *p++ = ':';
1376 *p++ = '='; *p++ = 'A' + i; *p++ = ':'; *p++ = '\\';
1377 WideCharToMultiByte(DRIVE_GetCodepage(i), 0, cwd[i], -1, p, 0x7fffffff, NULL, NULL);
1378 p += strlen(p) + 1;
1381 *p = 0;
1382 return env;
1386 /***********************************************************************
1387 * GetDiskFreeSpace (KERNEL.422)
1389 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
1390 LPDWORD sector_bytes, LPDWORD free_clusters,
1391 LPDWORD total_clusters )
1393 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
1394 free_clusters, total_clusters );
1398 /***********************************************************************
1399 * GetDiskFreeSpaceW (KERNEL32.@)
1401 * Fails if expression resulting from current drive's dir and "root"
1402 * is not a root dir of the target drive.
1404 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1405 * if the corresponding info is unneeded.
1407 * FIXME: needs to support UNC names from Win95 OSR2 on.
1409 * Behaviour under Win95a:
1410 * CurrDir root result
1411 * "E:\\TEST" "E:" FALSE
1412 * "E:\\" "E:" TRUE
1413 * "E:\\" "E" FALSE
1414 * "E:\\" "\\" TRUE
1415 * "E:\\TEST" "\\" TRUE
1416 * "E:\\TEST" ":\\" FALSE
1417 * "E:\\TEST" "E:\\" TRUE
1418 * "E:\\TEST" "" FALSE
1419 * "E:\\" "" FALSE (!)
1420 * "E:\\" 0x0 TRUE
1421 * "E:\\TEST" 0x0 TRUE (!)
1422 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1423 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1425 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1426 LPDWORD sector_bytes, LPDWORD free_clusters,
1427 LPDWORD total_clusters )
1429 int drive, sec_size;
1430 ULARGE_INTEGER size,available;
1431 LPCWSTR path;
1432 DWORD cluster_sec;
1434 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root), cluster_sectors, sector_bytes,
1435 free_clusters, total_clusters);
1437 if (!root || root[0] == '\\' || root[0] == '/')
1438 drive = DRIVE_GetCurrentDrive();
1439 else
1440 if (root[0] && root[1] == ':') /* root contains drive tag */
1442 drive = toupperW(root[0]) - 'A';
1443 path = &root[2];
1444 if (path[0] == '\0')
1446 path = DRIVE_GetDosCwd(drive);
1447 if (!path)
1449 SetLastError(ERROR_PATH_NOT_FOUND);
1450 return FALSE;
1453 else
1454 if (path[0] == '\\')
1455 path++;
1457 if (path[0]) /* oops, we are in a subdir */
1459 SetLastError(ERROR_INVALID_NAME);
1460 return FALSE;
1463 else
1465 if (!root[0])
1466 SetLastError(ERROR_PATH_NOT_FOUND);
1467 else
1468 SetLastError(ERROR_INVALID_NAME);
1469 return FALSE;
1472 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1474 /* Cap the size and available at 2GB as per specs. */
1475 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1477 size.s.HighPart = 0;
1478 size.s.LowPart = 0x7fffffff;
1480 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1482 available.s.HighPart =0;
1483 available.s.LowPart = 0x7fffffff;
1485 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1486 size.s.LowPart /= sec_size;
1487 available.s.LowPart /= sec_size;
1488 /* FIXME: probably have to adjust those variables too for CDFS */
1489 cluster_sec = 1;
1490 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1492 if (cluster_sectors)
1493 *cluster_sectors = cluster_sec;
1494 if (sector_bytes)
1495 *sector_bytes = sec_size;
1496 if (free_clusters)
1497 *free_clusters = available.s.LowPart / cluster_sec;
1498 if (total_clusters)
1499 *total_clusters = size.s.LowPart / cluster_sec;
1500 return TRUE;
1504 /***********************************************************************
1505 * GetDiskFreeSpaceA (KERNEL32.@)
1507 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1508 LPDWORD sector_bytes, LPDWORD free_clusters,
1509 LPDWORD total_clusters )
1511 UNICODE_STRING rootW;
1512 BOOL ret = FALSE;
1514 if (root)
1516 if(!RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1518 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1519 return FALSE;
1522 else
1523 rootW.Buffer = NULL;
1525 ret = GetDiskFreeSpaceW(rootW.Buffer, cluster_sectors, sector_bytes,
1526 free_clusters, total_clusters );
1527 RtlFreeUnicodeString(&rootW);
1529 return ret;
1533 /***********************************************************************
1534 * GetDiskFreeSpaceExW (KERNEL32.@)
1536 * This function is used to acquire the size of the available and
1537 * total space on a logical volume.
1539 * RETURNS
1541 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1542 * detailed error information.
1545 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root,
1546 PULARGE_INTEGER avail,
1547 PULARGE_INTEGER total,
1548 PULARGE_INTEGER totalfree)
1550 int drive;
1551 ULARGE_INTEGER size,available;
1553 if (!root) drive = DRIVE_GetCurrentDrive();
1554 else
1555 { /* C: always works for GetDiskFreeSpaceEx */
1556 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
1558 FIXME("there are valid root names which are not supported yet\n");
1559 /* ..like UNC names, for instance. */
1561 WARN("invalid root '%s'\n", debugstr_w(root));
1562 return FALSE;
1564 drive = toupperW(root[0]) - 'A';
1567 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1569 if (total)
1571 total->s.HighPart = size.s.HighPart;
1572 total->s.LowPart = size.s.LowPart;
1575 if (totalfree)
1577 totalfree->s.HighPart = available.s.HighPart;
1578 totalfree->s.LowPart = available.s.LowPart;
1581 if (avail)
1583 if (FIXME_ON(dosfs))
1585 /* On Windows2000, we need to check the disk quota
1586 allocated for the user owning the calling process. We
1587 don't want to be more obtrusive than necessary with the
1588 FIXME messages, so don't print the FIXME unless Wine is
1589 actually masquerading as Windows2000. */
1591 OSVERSIONINFOA ovi;
1592 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1593 if (GetVersionExA(&ovi))
1595 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1596 FIXME("no per-user quota support yet\n");
1600 /* Quick hack, should eventually be fixed to work 100% with
1601 Windows2000 (see comment above). */
1602 avail->s.HighPart = available.s.HighPart;
1603 avail->s.LowPart = available.s.LowPart;
1606 return TRUE;
1609 /***********************************************************************
1610 * GetDiskFreeSpaceExA (KERNEL32.@)
1612 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
1613 PULARGE_INTEGER total,
1614 PULARGE_INTEGER totalfree)
1616 UNICODE_STRING rootW;
1617 BOOL ret;
1619 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
1620 else rootW.Buffer = NULL;
1622 ret = GetDiskFreeSpaceExW( rootW.Buffer, avail, total, totalfree);
1624 RtlFreeUnicodeString(&rootW);
1625 return ret;
1628 /***********************************************************************
1629 * GetDriveType (KERNEL.136)
1630 * This function returns the type of a drive in Win16.
1631 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1632 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1633 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1634 * do any pseudo-clever changes.
1636 * RETURNS
1637 * drivetype DRIVE_xxx
1639 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1641 UINT type = DRIVE_GetType(drive);
1642 TRACE("(%c:)\n", 'A' + drive );
1643 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1644 return type;
1648 /***********************************************************************
1649 * GetDriveTypeW (KERNEL32.@)
1651 * Returns the type of the disk drive specified. If root is NULL the
1652 * root of the current directory is used.
1654 * RETURNS
1656 * Type of drive (from Win32 SDK):
1658 * DRIVE_UNKNOWN unable to find out anything about the drive
1659 * DRIVE_NO_ROOT_DIR nonexistent root dir
1660 * DRIVE_REMOVABLE the disk can be removed from the machine
1661 * DRIVE_FIXED the disk can not be removed from the machine
1662 * DRIVE_REMOTE network disk
1663 * DRIVE_CDROM CDROM drive
1664 * DRIVE_RAMDISK virtual disk in RAM
1666 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1668 int drive;
1669 TRACE("(%s)\n", debugstr_w(root));
1671 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1672 else
1674 if ((root[1]) && (root[1] != ':'))
1676 WARN("invalid root %s\n", debugstr_w(root));
1677 return DRIVE_NO_ROOT_DIR;
1679 drive = toupperW(root[0]) - 'A';
1681 return DRIVE_GetType(drive);
1685 /***********************************************************************
1686 * GetDriveTypeA (KERNEL32.@)
1688 UINT WINAPI GetDriveTypeA( LPCSTR root )
1690 UNICODE_STRING rootW;
1691 UINT ret = 0;
1693 if (root)
1695 if( !RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1697 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1698 return 0;
1701 else
1702 rootW.Buffer = NULL;
1704 ret = GetDriveTypeW(rootW.Buffer);
1706 RtlFreeUnicodeString(&rootW);
1707 return ret;
1712 /***********************************************************************
1713 * GetCurrentDirectory (KERNEL.411)
1715 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1717 WCHAR cur_dirW[MAX_PATH];
1719 DRIVE_GetCurrentDirectory(MAX_PATH, cur_dirW);
1720 return (UINT16)WideCharToMultiByte(CP_ACP, 0, cur_dirW, -1, buf, buflen, NULL, NULL);
1724 /***********************************************************************
1725 * GetCurrentDirectoryW (KERNEL32.@)
1727 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1729 UINT ret;
1730 WCHAR longname[MAX_PATHNAME_LEN];
1731 WCHAR shortname[MAX_PATHNAME_LEN];
1733 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1734 if ( ret > MAX_PATHNAME_LEN ) {
1735 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1736 return ret;
1738 GetLongPathNameW(shortname, longname, MAX_PATHNAME_LEN);
1739 ret = strlenW( longname ) + 1;
1740 if (ret > buflen) return ret;
1741 strcpyW(buf, longname);
1742 return ret - 1;
1745 /***********************************************************************
1746 * GetCurrentDirectoryA (KERNEL32.@)
1748 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1750 WCHAR bufferW[MAX_PATH];
1751 DWORD ret, retW;
1753 retW = GetCurrentDirectoryW(MAX_PATH, bufferW);
1755 if (!retW)
1756 ret = 0;
1757 else if (retW > MAX_PATH)
1759 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1760 ret = 0;
1762 else
1764 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1765 if (buflen >= ret)
1767 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, buflen, NULL, NULL);
1768 ret--; /* length without 0 */
1771 return ret;
1775 /***********************************************************************
1776 * SetCurrentDirectory (KERNEL.412)
1778 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1780 return SetCurrentDirectoryA( dir );
1784 /***********************************************************************
1785 * SetCurrentDirectoryW (KERNEL32.@)
1787 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dir )
1789 int drive, olddrive = DRIVE_GetCurrentDrive();
1791 if (!dir)
1793 SetLastError(ERROR_INVALID_PARAMETER);
1794 return FALSE;
1796 if (dir[0] && (dir[1]==':'))
1798 drive = toupperW( *dir ) - 'A';
1799 dir += 2;
1801 else
1802 drive = olddrive;
1804 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1805 sets pTask->curdir only if pTask->curdrive is drive */
1806 if (!(DRIVE_SetCurrentDrive( drive )))
1807 return FALSE;
1809 /* FIXME: what about empty strings? Add a \\ ? */
1810 if (!DRIVE_Chdir( drive, dir )) {
1811 DRIVE_SetCurrentDrive(olddrive);
1812 return FALSE;
1814 return TRUE;
1818 /***********************************************************************
1819 * SetCurrentDirectoryA (KERNEL32.@)
1821 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1823 UNICODE_STRING dirW;
1824 BOOL ret = FALSE;
1826 if (!dir)
1828 SetLastError(ERROR_INVALID_PARAMETER);
1829 return FALSE;
1832 if (RtlCreateUnicodeStringFromAsciiz(&dirW, dir))
1834 ret = SetCurrentDirectoryW(dirW.Buffer);
1835 RtlFreeUnicodeString(&dirW);
1837 else
1838 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1839 return ret;
1843 /***********************************************************************
1844 * GetLogicalDriveStringsA (KERNEL32.@)
1846 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1848 int drive, count;
1850 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1851 if (DRIVE_IsValid(drive)) count++;
1852 if ((count * 4) + 1 <= len)
1854 LPSTR p = buffer;
1855 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1856 if (DRIVE_IsValid(drive))
1858 *p++ = 'a' + drive;
1859 *p++ = ':';
1860 *p++ = '\\';
1861 *p++ = '\0';
1863 *p = '\0';
1864 return count * 4;
1866 else
1867 return (count * 4) + 1; /* account for terminating null */
1868 /* The API tells about these different return values */
1872 /***********************************************************************
1873 * GetLogicalDriveStringsW (KERNEL32.@)
1875 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1877 int drive, count;
1879 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1880 if (DRIVE_IsValid(drive)) count++;
1881 if (count * 4 * sizeof(WCHAR) <= len)
1883 LPWSTR p = buffer;
1884 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1885 if (DRIVE_IsValid(drive))
1887 *p++ = (WCHAR)('a' + drive);
1888 *p++ = (WCHAR)':';
1889 *p++ = (WCHAR)'\\';
1890 *p++ = (WCHAR)'\0';
1892 *p = (WCHAR)'\0';
1894 return count * 4 * sizeof(WCHAR);
1898 /***********************************************************************
1899 * GetLogicalDrives (KERNEL32.@)
1901 DWORD WINAPI GetLogicalDrives(void)
1903 DWORD ret = 0;
1904 int drive;
1906 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1908 if ( (DRIVE_IsValid(drive)) ||
1909 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1910 ret |= (1 << drive);
1912 return ret;
1916 /***********************************************************************
1917 * GetVolumeInformationW (KERNEL32.@)
1919 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1920 DWORD label_len, DWORD *serial,
1921 DWORD *filename_len, DWORD *flags,
1922 LPWSTR fsname, DWORD fsname_len )
1924 int drive;
1925 LPWSTR cp;
1927 /* FIXME, SetLastError()s missing */
1929 if (!root) drive = DRIVE_GetCurrentDrive();
1930 else
1932 if (root[0] && root[1] != ':')
1934 WARN("invalid root %s\n", debugstr_w(root));
1935 return FALSE;
1937 drive = toupperW(root[0]) - 'A';
1939 if (!DRIVE_IsValid( drive )) return FALSE;
1940 if (label && label_len)
1942 strncpyW( label, DRIVE_GetLabel(drive), label_len );
1943 label[label_len - 1] = 0; /* ensure 0 termination */
1944 cp = label + strlenW(label);
1945 while (cp != label && *(cp-1) == ' ') cp--;
1946 *cp = '\0';
1948 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1950 /* Set the filesystem information */
1951 /* Note: we only emulate a FAT fs at present */
1953 if (filename_len) {
1954 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1955 *filename_len = 12;
1956 else
1957 *filename_len = 255;
1959 if (flags)
1961 *flags=0;
1962 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1963 *flags|=FS_CASE_SENSITIVE;
1964 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1965 *flags|=FS_CASE_IS_PRESERVED;
1967 if (fsname && fsname_len)
1969 /* Diablo checks that return code ... */
1970 if (DOSDrives[drive].type == DRIVE_CDROM)
1972 static const WCHAR cdfsW[] = {'C','D','F','S',0};
1973 strncpyW( fsname, cdfsW, fsname_len );
1975 else
1977 static const WCHAR fatW[] = {'F','A','T',0};
1978 strncpyW( fsname, fatW, fsname_len );
1980 fsname[fsname_len - 1] = 0; /* ensure 0 termination */
1982 return TRUE;
1986 /***********************************************************************
1987 * GetVolumeInformationA (KERNEL32.@)
1989 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1990 DWORD label_len, DWORD *serial,
1991 DWORD *filename_len, DWORD *flags,
1992 LPSTR fsname, DWORD fsname_len )
1994 UNICODE_STRING rootW;
1995 LPWSTR labelW, fsnameW;
1996 BOOL ret;
1998 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
1999 else rootW.Buffer = NULL;
2000 labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
2001 fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
2003 if ((ret = GetVolumeInformationW(rootW.Buffer, labelW, label_len, serial,
2004 filename_len, flags, fsnameW, fsname_len)))
2006 if (label) WideCharToMultiByte(CP_ACP, 0, labelW, -1, label, label_len, NULL, NULL);
2007 if (fsname) WideCharToMultiByte(CP_ACP, 0, fsnameW, -1, fsname, fsname_len, NULL, NULL);
2010 RtlFreeUnicodeString(&rootW);
2011 if (labelW) HeapFree( GetProcessHeap(), 0, labelW );
2012 if (fsnameW) HeapFree( GetProcessHeap(), 0, fsnameW );
2013 return ret;
2016 /***********************************************************************
2017 * SetVolumeLabelW (KERNEL32.@)
2019 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR volname )
2021 int drive;
2023 /* FIXME, SetLastErrors missing */
2025 if (!root) drive = DRIVE_GetCurrentDrive();
2026 else
2028 if ((root[1]) && (root[1] != ':'))
2030 WARN("invalid root %s\n", debugstr_w(root));
2031 return FALSE;
2033 drive = toupperW(root[0]) - 'A';
2035 if (!DRIVE_IsValid( drive )) return FALSE;
2037 /* some copy protection stuff check this */
2038 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
2040 strncpyW(DOSDrives[drive].label_conf, volname, 12);
2041 DOSDrives[drive].label_conf[12 - 1] = 0; /* ensure 0 termination */
2042 return TRUE;
2045 /***********************************************************************
2046 * SetVolumeLabelA (KERNEL32.@)
2048 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
2050 UNICODE_STRING rootW, volnameW;
2051 BOOL ret;
2053 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2054 else rootW.Buffer = NULL;
2055 if (volname) RtlCreateUnicodeStringFromAsciiz(&volnameW, volname);
2056 else volnameW.Buffer = NULL;
2058 ret = SetVolumeLabelW( rootW.Buffer, volnameW.Buffer );
2060 RtlFreeUnicodeString(&rootW);
2061 RtlFreeUnicodeString(&volnameW);
2062 return ret;