Add some missing HeapFree's and one missing free.
[wine/wine-kai.git] / files / drive.c
blob203dd1879f5bc537bccd4c679e4b90e0b3650136
1 /*
2 * DOS drives handling functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
7 * Label & serial number read support.
8 * (c) 1999 Petr Tomasek <tomasek@etf.cuni.cz>
9 * (c) 2000 Andreas Mohr (changes)
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "config.h"
27 #include "wine/port.h"
29 #include <assert.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #ifdef HAVE_UNISTD_H
39 # include <unistd.h>
40 #endif
42 #ifdef HAVE_SYS_PARAM_H
43 # include <sys/param.h>
44 #endif
45 #ifdef STATFS_DEFINED_BY_SYS_VFS
46 # include <sys/vfs.h>
47 #else
48 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
49 # include <sys/mount.h>
50 # else
51 # ifdef STATFS_DEFINED_BY_SYS_STATFS
52 # include <sys/statfs.h>
53 # endif
54 # endif
55 #endif
57 #define NONAMELESSUNION
58 #define NONAMELESSSTRUCT
59 #include "winbase.h"
60 #include "winternl.h"
61 #include "wine/winbase16.h" /* for GetCurrentTask */
62 #include "winerror.h"
63 #include "winioctl.h"
64 #include "ntddstor.h"
65 #include "ntddcdrm.h"
66 #include "drive.h"
67 #include "file.h"
68 #include "msdos.h"
69 #include "task.h"
70 #include "wine/unicode.h"
71 #include "wine/library.h"
72 #include "wine/server.h"
73 #include "wine/debug.h"
75 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
76 WINE_DECLARE_DEBUG_CHANNEL(file);
78 typedef struct
80 char *root; /* root dir in Unix format without trailing / */
81 LPWSTR dos_cwd; /* cwd in DOS format without leading or trailing \ */
82 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
83 char *device; /* raw device path */
84 WCHAR label_conf[12]; /* drive label as cfg'd in wine config */
85 WCHAR label_read[12]; /* drive label as read from device */
86 DWORD serial_conf; /* drive serial number as cfg'd in wine config */
87 UINT type; /* drive type */
88 UINT flags; /* drive flags */
89 UINT codepage; /* drive code page */
90 dev_t dev; /* unix device number */
91 ino_t ino; /* unix inode number */
92 } DOSDRIVE;
95 static const WCHAR DRIVE_Types[][8] =
97 { 0 }, /* DRIVE_UNKNOWN */
98 { 0 }, /* DRIVE_NO_ROOT_DIR */
99 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
100 {'h','d',0}, /* DRIVE_FIXED */
101 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
102 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
103 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
107 /* Known filesystem types */
109 typedef struct
111 const WCHAR name[6];
112 UINT flags;
113 } FS_DESCR;
115 static const FS_DESCR DRIVE_Filesystems[] =
117 { {'u','n','i','x',0}, DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
118 { {'m','s','d','o','s',0}, DRIVE_SHORT_NAMES },
119 { {'d','o','s',0}, DRIVE_SHORT_NAMES },
120 { {'f','a','t',0}, DRIVE_SHORT_NAMES },
121 { {'v','f','a','t',0}, DRIVE_CASE_PRESERVING },
122 { {'w','i','n','9','5',0}, DRIVE_CASE_PRESERVING },
123 { { 0 }, 0 }
127 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
128 static int DRIVE_CurDrive = -1;
130 static HTASK16 DRIVE_LastTask = 0;
132 /* strdup on the process heap */
133 inline static char *heap_strdup( const char *str )
135 INT len = strlen(str) + 1;
136 LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
137 if (p) memcpy( p, str, len );
138 return p;
141 extern void CDROM_InitRegistry(int dev);
143 /***********************************************************************
144 * DRIVE_GetDriveType
146 static UINT DRIVE_GetDriveType( LPCWSTR name )
148 WCHAR buffer[20];
149 int i;
150 static const WCHAR TypeW[] = {'T','y','p','e',0};
151 static const WCHAR hdW[] = {'h','d',0};
153 PROFILE_GetWineIniString( name, TypeW, hdW, buffer, 20 );
154 if(!buffer[0])
155 strcpyW(buffer,hdW);
156 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
158 if (!strcmpiW( buffer, DRIVE_Types[i] )) return i;
160 MESSAGE("%s: unknown drive type %s, defaulting to 'hd'.\n",
161 debugstr_w(name), debugstr_w(buffer) );
162 return DRIVE_FIXED;
166 /***********************************************************************
167 * DRIVE_GetFSFlags
169 static UINT DRIVE_GetFSFlags( LPCWSTR name, LPCWSTR value )
171 const FS_DESCR *descr;
173 for (descr = DRIVE_Filesystems; *descr->name; descr++)
174 if (!strcmpiW( value, descr->name )) return descr->flags;
175 MESSAGE("%s: unknown filesystem type %s, defaulting to 'win95'.\n",
176 debugstr_w(name), debugstr_w(value) );
177 return DRIVE_CASE_PRESERVING;
181 /***********************************************************************
182 * DRIVE_Init
184 int DRIVE_Init(void)
186 int i, len, count = 0;
187 WCHAR name[] = {'D','r','i','v','e',' ','A',0};
188 WCHAR drive_env[] = {'=','A',':',0};
189 WCHAR path[MAX_PATHNAME_LEN];
190 WCHAR buffer[80];
191 struct stat drive_stat_buffer;
192 WCHAR *p;
193 DOSDRIVE *drive;
194 static const WCHAR PathW[] = {'P','a','t','h',0};
195 static const WCHAR empty_strW[] = { 0 };
196 static const WCHAR CodepageW[] = {'C','o','d','e','p','a','g','e',0};
197 static const WCHAR LabelW[] = {'L','a','b','e','l',0};
198 static const WCHAR SerialW[] = {'S','e','r','i','a','l',0};
199 static const WCHAR zeroW[] = {'0',0};
200 static const WCHAR def_serialW[] = {'1','2','3','4','5','6','7','8',0};
201 static const WCHAR FilesystemW[] = {'F','i','l','e','s','y','s','t','e','m',0};
202 static const WCHAR win95W[] = {'w','i','n','9','5',0};
203 static const WCHAR DeviceW[] = {'D','e','v','i','c','e',0};
204 static const WCHAR ReadVolInfoW[] = {'R','e','a','d','V','o','l','I','n','f','o',0};
205 static const WCHAR FailReadOnlyW[] = {'F','a','i','l','R','e','a','d','O','n','l','y',0};
206 static const WCHAR driveC_labelW[] = {'D','r','i','v','e',' ','C',' ',' ',' ',' ',0};
208 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
210 PROFILE_GetWineIniString( name, PathW, empty_strW, path, MAX_PATHNAME_LEN );
211 if (path[0])
213 /* Get the code page number */
214 PROFILE_GetWineIniString( name, CodepageW, zeroW, /* 0 == CP_ACP */
215 buffer, 80 );
216 drive->codepage = strtolW( buffer, NULL, 10 );
218 p = path + strlenW(path) - 1;
219 while ((p > path) && (*p == '/')) *p-- = '\0';
221 if (path[0] == '/')
223 len = WideCharToMultiByte(drive->codepage, 0, path, -1, NULL, 0, NULL, NULL);
224 drive->root = HeapAlloc(GetProcessHeap(), 0, len);
225 WideCharToMultiByte(drive->codepage, 0, path, -1, drive->root, len, NULL, NULL);
227 else
229 /* relative paths are relative to config dir */
230 const char *config = wine_get_config_dir();
231 len = strlen(config);
232 len += WideCharToMultiByte(drive->codepage, 0, path, -1, NULL, 0, NULL, NULL) + 2;
233 drive->root = HeapAlloc( GetProcessHeap(), 0, len );
234 len -= sprintf( drive->root, "%s/", config );
235 WideCharToMultiByte(drive->codepage, 0, path, -1, drive->root + strlen(drive->root), len, NULL, NULL);
238 if (stat( drive->root, &drive_stat_buffer ))
240 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
241 drive->root, strerror(errno), 'A' + i);
242 HeapFree( GetProcessHeap(), 0, drive->root );
243 drive->root = NULL;
244 continue;
246 if (!S_ISDIR(drive_stat_buffer.st_mode))
248 MESSAGE("%s is not a directory, ignoring drive %c:\n",
249 drive->root, 'A' + i );
250 HeapFree( GetProcessHeap(), 0, drive->root );
251 drive->root = NULL;
252 continue;
255 drive->dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(drive->dos_cwd[0]));
256 drive->unix_cwd = heap_strdup( "" );
257 drive->type = DRIVE_GetDriveType( name );
258 drive->device = NULL;
259 drive->flags = 0;
260 drive->dev = drive_stat_buffer.st_dev;
261 drive->ino = drive_stat_buffer.st_ino;
263 /* Get the drive label */
264 PROFILE_GetWineIniString( name, LabelW, empty_strW, drive->label_conf, 12 );
265 if ((len = strlenW(drive->label_conf)) < 11)
267 /* Pad label with spaces */
268 while(len < 11) drive->label_conf[len++] = ' ';
269 drive->label_conf[11] = '\0';
272 /* Get the serial number */
273 PROFILE_GetWineIniString( name, SerialW, def_serialW, buffer, 80 );
274 drive->serial_conf = strtoulW( buffer, NULL, 16 );
276 /* Get the filesystem type */
277 PROFILE_GetWineIniString( name, FilesystemW, win95W, buffer, 80 );
278 drive->flags = DRIVE_GetFSFlags( name, buffer );
280 /* Get the device */
281 PROFILE_GetWineIniString( name, DeviceW, empty_strW, buffer, 80 );
282 if (buffer[0])
284 int cd_fd;
285 len = WideCharToMultiByte(CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL);
286 drive->device = HeapAlloc(GetProcessHeap(), 0, len);
287 WideCharToMultiByte(drive->codepage, 0, buffer, -1, drive->device, len, NULL, NULL);
289 if (PROFILE_GetWineIniBool( name, ReadVolInfoW, 1))
290 drive->flags |= DRIVE_READ_VOL_INFO;
292 if (drive->type == DRIVE_CDROM)
294 if ((cd_fd = open(drive->device, O_RDONLY|O_NONBLOCK)) != -1)
296 CDROM_InitRegistry(cd_fd);
297 close(cd_fd);
302 /* Get the FailReadOnly flag */
303 if (PROFILE_GetWineIniBool( name, FailReadOnlyW, 0 ))
304 drive->flags |= DRIVE_FAIL_READ_ONLY;
306 /* Make the first hard disk the current drive */
307 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
308 DRIVE_CurDrive = i;
310 count++;
311 TRACE("%s: path=%s type=%s label=%s serial=%08lx "
312 "flags=%08x codepage=%u dev=%x ino=%x\n",
313 debugstr_w(name), drive->root, debugstr_w(DRIVE_Types[drive->type]),
314 debugstr_w(drive->label_conf), drive->serial_conf, drive->flags,
315 drive->codepage, (int)drive->dev, (int)drive->ino );
317 else WARN("%s: not defined\n", debugstr_w(name) );
320 if (!count)
322 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
323 /* Create a C drive pointing to Unix root dir */
324 DOSDrives[2].root = heap_strdup( "/" );
325 DOSDrives[2].dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DOSDrives[2].dos_cwd[0]));
326 DOSDrives[2].unix_cwd = heap_strdup( "" );
327 strcpyW( DOSDrives[2].label_conf, driveC_labelW );
328 DOSDrives[2].serial_conf = 12345678;
329 DOSDrives[2].type = DRIVE_FIXED;
330 DOSDrives[2].device = NULL;
331 DOSDrives[2].flags = 0;
332 DRIVE_CurDrive = 2;
335 /* Make sure the current drive is valid */
336 if (DRIVE_CurDrive == -1)
338 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
340 if (drive->root && !(drive->flags & DRIVE_DISABLED))
342 DRIVE_CurDrive = i;
343 break;
348 /* get current working directory info for all drives */
349 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
351 if (!GetEnvironmentVariableW(drive_env, path, MAX_PATHNAME_LEN)) continue;
352 /* sanity check */
353 if (toupperW(path[0]) != drive_env[1] || path[1] != ':') continue;
354 DRIVE_Chdir( i, path + 2 );
356 return 1;
360 /***********************************************************************
361 * DRIVE_IsValid
363 int DRIVE_IsValid( int drive )
365 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
366 return (DOSDrives[drive].root &&
367 !(DOSDrives[drive].flags & DRIVE_DISABLED));
371 /***********************************************************************
372 * DRIVE_GetCurrentDrive
374 int DRIVE_GetCurrentDrive(void)
376 TDB *pTask = TASK_GetCurrent();
377 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
378 return DRIVE_CurDrive;
382 /***********************************************************************
383 * DRIVE_SetCurrentDrive
385 int DRIVE_SetCurrentDrive( int drive )
387 TDB *pTask = TASK_GetCurrent();
388 if (!DRIVE_IsValid( drive ))
390 SetLastError( ERROR_INVALID_DRIVE );
391 return 0;
393 TRACE("%c:\n", 'A' + drive );
394 DRIVE_CurDrive = drive;
395 if (pTask) pTask->curdrive = drive | 0x80;
396 return 1;
400 /***********************************************************************
401 * DRIVE_FindDriveRoot
403 * Find a drive for which the root matches the beginning of the given path.
404 * This can be used to translate a Unix path into a drive + DOS path.
405 * Return value is the drive, or -1 on error. On success, path is modified
406 * to point to the beginning of the DOS path.
408 * Note: path must be in the encoding of the underlying Unix file system.
410 int DRIVE_FindDriveRoot( const char **path )
412 /* Starting with the full path, check if the device and inode match any of
413 * the wine 'drives'. If not then remove the last path component and try
414 * again. If the last component was a '..' then skip a normal component
415 * since it's a directory that's ascended back out of.
417 int drive, level, len;
418 char buffer[MAX_PATHNAME_LEN];
419 char *p;
420 struct stat st;
422 strcpy( buffer, *path );
423 while ((p = strchr( buffer, '\\' )) != NULL)
424 *p = '/';
425 len = strlen(buffer);
427 /* strip off trailing slashes */
428 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
430 for (;;)
432 /* Find the drive */
433 if (stat( buffer, &st ) == 0 && S_ISDIR( st.st_mode ))
435 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
437 if (!DOSDrives[drive].root ||
438 (DOSDrives[drive].flags & DRIVE_DISABLED))
439 continue;
441 if ((DOSDrives[drive].dev == st.st_dev) &&
442 (DOSDrives[drive].ino == st.st_ino))
444 if (len == 1) len = 0; /* preserve root slash in returned path */
445 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
446 *path, 'A' + drive, buffer, *path + len);
447 *path += len;
448 if (!**path) *path = "\\";
449 return drive;
453 if (len <= 1) return -1; /* reached root */
455 level = 0;
456 while (level < 1)
458 /* find start of the last path component */
459 while (len > 1 && buffer[len - 1] != '/') len--;
460 if (!buffer[len]) break; /* empty component -> reached root */
461 /* does removing it take us up a level? */
462 if (strcmp( buffer + len, "." ) != 0)
463 level += strcmp( buffer + len, ".." ) ? 1 : -1;
464 buffer[len] = 0;
465 /* strip off trailing slashes */
466 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
472 /***********************************************************************
473 * DRIVE_FindDriveRootW
475 * Unicode version of DRIVE_FindDriveRoot.
477 int DRIVE_FindDriveRootW( LPCWSTR *path )
479 int drive, rootdrive = -1;
480 char buffer[MAX_PATHNAME_LEN];
481 LPCWSTR p = *path;
482 int len, match_len = -1;
484 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
486 if (!DOSDrives[drive].root ||
487 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
489 WideCharToMultiByte(DOSDrives[drive].codepage, 0, *path, -1,
490 buffer, MAX_PATHNAME_LEN, NULL, NULL);
492 len = strlen(DOSDrives[drive].root);
493 if(strncmp(DOSDrives[drive].root, buffer, len))
494 continue;
495 if(len <= match_len) continue;
496 match_len = len;
497 rootdrive = drive;
498 p = *path + len;
501 if (rootdrive != -1)
503 *path = p;
504 TRACE("%s -> drive %c:, root='%s', name=%s\n",
505 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, debugstr_w(*path) );
507 return rootdrive;
511 /***********************************************************************
512 * DRIVE_GetRoot
514 const char * DRIVE_GetRoot( int drive )
516 if (!DRIVE_IsValid( drive )) return NULL;
517 return DOSDrives[drive].root;
521 /***********************************************************************
522 * DRIVE_GetDosCwd
524 LPCWSTR DRIVE_GetDosCwd( int drive )
526 TDB *pTask = TASK_GetCurrent();
527 if (!DRIVE_IsValid( drive )) return NULL;
529 /* Check if we need to change the directory to the new task. */
530 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
531 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
532 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
534 static const WCHAR rootW[] = {'\\',0};
535 WCHAR curdirW[MAX_PATH];
536 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
537 /* Perform the task-switch */
538 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
539 DRIVE_LastTask = GetCurrentTask();
541 return DOSDrives[drive].dos_cwd;
545 /***********************************************************************
546 * DRIVE_GetUnixCwd
548 const char * DRIVE_GetUnixCwd( int drive )
550 TDB *pTask = TASK_GetCurrent();
551 if (!DRIVE_IsValid( drive )) return NULL;
553 /* Check if we need to change the directory to the new task. */
554 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
555 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
556 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
558 static const WCHAR rootW[] = {'\\',0};
559 WCHAR curdirW[MAX_PATH];
560 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
561 /* Perform the task-switch */
562 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
563 DRIVE_LastTask = GetCurrentTask();
565 return DOSDrives[drive].unix_cwd;
569 /***********************************************************************
570 * DRIVE_GetDevice
572 const char * DRIVE_GetDevice( int drive )
574 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
577 /******************************************************************
578 * static WORD CDROM_Data_FindBestVoldesc
582 static WORD CDROM_Data_FindBestVoldesc(int fd)
584 BYTE cur_vd_type, max_vd_type = 0;
585 unsigned int offs, best_offs = 0, extra_offs = 0;
586 char sig[3];
588 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
590 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
591 * the volume label is displaced forward by 8
593 lseek(fd, offs + 11, SEEK_SET); /* check for non-ISO9660 signature */
594 read(fd, &sig, 3);
595 if ((sig[0] == 'R') && (sig[1] == 'O') && (sig[2]=='M'))
597 extra_offs = 8;
599 lseek(fd, offs + extra_offs, SEEK_SET);
600 read(fd, &cur_vd_type, 1);
601 if (cur_vd_type == 0xff) /* voldesc set terminator */
602 break;
603 if (cur_vd_type > max_vd_type)
605 max_vd_type = cur_vd_type;
606 best_offs = offs + extra_offs;
609 return best_offs;
612 /***********************************************************************
613 * DRIVE_ReadSuperblock
615 * NOTE
616 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
617 * to check, that they are writing on a FAT filesystem !
619 int DRIVE_ReadSuperblock (int drive, char * buff)
621 #define DRIVE_SUPER 96
622 int fd;
623 off_t offs;
624 int ret = 0;
626 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
627 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
629 struct stat st;
630 if (!DOSDrives[drive].device)
631 ERR("No device configured for drive %c: !\n", 'A'+drive);
632 else
633 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
634 (stat(DOSDrives[drive].device, &st)) ?
635 "not available or symlink not valid ?" : "no permission");
636 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
637 PROFILE_UsageWineIni();
638 return -1;
641 switch(DOSDrives[drive].type)
643 case DRIVE_REMOVABLE:
644 case DRIVE_FIXED:
645 offs = 0;
646 break;
647 case DRIVE_CDROM:
648 offs = CDROM_Data_FindBestVoldesc(fd);
649 break;
650 default:
651 offs = 0;
652 break;
655 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs))
657 ret = -4;
658 goto the_end;
660 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER)
662 ret = -2;
663 goto the_end;
666 switch(DOSDrives[drive].type)
668 case DRIVE_REMOVABLE:
669 case DRIVE_FIXED:
670 if ((buff[0x26]!=0x29) || /* Check for FAT present */
671 /* FIXME: do really all FAT have their name beginning with
672 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
673 memcmp( buff+0x36,"FAT",3))
675 ERR("The filesystem is not FAT !! (device=%s)\n",
676 DOSDrives[drive].device);
677 ret = -3;
678 goto the_end;
680 break;
681 case DRIVE_CDROM:
682 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
684 ret = -3;
685 goto the_end;
687 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
688 break;
689 default:
690 ret = -3;
691 goto the_end;
694 return close(fd);
695 the_end:
696 close(fd);
697 return ret;
701 /***********************************************************************
702 * DRIVE_WriteSuperblockEntry
704 * NOTE
705 * We are writing as little as possible (ie. not the whole SuperBlock)
706 * not to interfere with kernel. The drive can be mounted !
708 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
710 int fd;
712 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
714 ERR("Cannot open the device %s (for writing)\n",
715 DOSDrives[drive].device);
716 return -1;
718 if (lseek(fd,ofs,SEEK_SET)!=ofs)
720 ERR("lseek failed on device %s !\n",
721 DOSDrives[drive].device);
722 close(fd);
723 return -2;
725 if (write(fd,buff,len)!=len)
727 close(fd);
728 ERR("Cannot write on %s !\n",
729 DOSDrives[drive].device);
730 return -3;
732 return close (fd);
735 /******************************************************************
736 * static HANDLE CDROM_Open
740 static HANDLE CDROM_Open(int drive)
742 WCHAR root[] = {'\\','\\','.','\\','A',':',0};
743 root[4] += drive;
744 return CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
747 /**************************************************************************
748 * CDROM_Data_GetLabel [internal]
750 DWORD CDROM_Data_GetLabel(int drive, WCHAR *label)
752 #define LABEL_LEN 32+1
753 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
754 WORD offs = CDROM_Data_FindBestVoldesc(dev);
755 WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
756 DWORD unicode_id = 0;
758 if (offs)
760 if ((lseek(dev, offs+0x58, SEEK_SET) == offs+0x58)
761 && (read(dev, &unicode_id, 3) == 3))
763 int ver = (unicode_id & 0xff0000) >> 16;
765 if ((lseek(dev, offs+0x28, SEEK_SET) != offs+0x28)
766 || (read(dev, &label_read, LABEL_LEN) != LABEL_LEN))
767 goto failure;
769 close(dev);
770 if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
771 && ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
772 { /* yippee, unicode */
773 int i;
774 WORD ch;
775 for (i=0; i<LABEL_LEN;i++)
776 { /* Motorola -> Intel Unicode conversion :-\ */
777 ch = label_read[i];
778 label_read[i] = (ch << 8) | (ch >> 8);
780 strncpyW(label, label_read, 11);
781 label[11] = 0;
783 else
785 MultiByteToWideChar(DOSDrives[drive].codepage, 0, (LPSTR)label_read, -1, label, 11);
786 label[11] = '\0';
788 return 1;
791 failure:
792 close(dev);
793 ERR("error reading label !\n");
794 return 0;
797 /**************************************************************************
798 * CDROM_GetLabel [internal]
800 static DWORD CDROM_GetLabel(int drive, WCHAR *label)
802 HANDLE h = CDROM_Open(drive);
803 CDROM_DISK_DATA cdd;
804 DWORD br;
805 DWORD ret = 1;
807 if (!h || !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
808 return 0;
810 switch (cdd.DiskData & 0x03)
812 case CDROM_DISK_DATA_TRACK:
813 if (!CDROM_Data_GetLabel(drive, label))
814 ret = 0;
815 break;
816 case CDROM_DISK_AUDIO_TRACK:
818 static const WCHAR audioCD[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
819 strcpyW(label, audioCD);
820 break;
822 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
823 FIXME("Need to get the label of a mixed mode CD!\n");
824 /* This assumes that the first track is a data track! */
825 /* I guess the correct way would be to enumerate all data tracks
826 and check each for the title */
827 if (!CDROM_Data_GetLabel(drive, label))
828 ret = 0;
829 break;
830 case 0:
831 ret = 0;
832 break;
834 TRACE("CD: label is %s\n", debugstr_w(label));
836 return ret;
838 /***********************************************************************
839 * DRIVE_GetLabel
841 LPCWSTR DRIVE_GetLabel( int drive )
843 int read = 0;
844 char buff[DRIVE_SUPER];
845 int offs = -1;
847 if (!DRIVE_IsValid( drive )) return NULL;
848 if (DOSDrives[drive].type == DRIVE_CDROM)
850 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
852 else
853 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
855 if (DRIVE_ReadSuperblock(drive,(char *) buff))
856 ERR("Invalid or unreadable superblock on %s (%c:).\n",
857 DOSDrives[drive].device, (char)(drive+'A'));
858 else {
859 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
860 DOSDrives[drive].type == DRIVE_FIXED)
861 offs = 0x2b;
863 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
864 if (offs != -1)
865 MultiByteToWideChar(DOSDrives[drive].codepage, 0, buff+offs, 11,
866 DOSDrives[drive].label_read, 11);
867 DOSDrives[drive].label_read[11]='\0';
868 read = 1;
872 return (read) ?
873 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
876 #define CDFRAMES_PERSEC 75
877 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
878 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
879 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
881 /**************************************************************************
882 * CDROM_Audio_GetSerial [internal]
884 static DWORD CDROM_Audio_GetSerial(HANDLE h)
886 unsigned long serial = 0;
887 int i;
888 WORD wMagic;
889 DWORD dwStart, dwEnd, br;
890 CDROM_TOC toc;
892 if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
893 return 0;
896 * wMagic collects the wFrames from track 1
897 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
898 * frames.
899 * There it is collected for correcting the serial when there are less than
900 * 3 tracks.
902 wMagic = toc.TrackData[0].Address[2];
903 dwStart = FRAME_OF_TOC(toc, toc.FirstTrack);
905 for (i = 0; i <= toc.LastTrack - toc.FirstTrack; i++) {
906 serial += (toc.TrackData[i].Address[0] << 16) |
907 (toc.TrackData[i].Address[1] << 8) | toc.TrackData[i].Address[2];
909 dwEnd = FRAME_OF_TOC(toc, toc.LastTrack + 1);
911 if (toc.LastTrack - toc.FirstTrack + 1 < 3)
912 serial += wMagic + (dwEnd - dwStart);
914 return serial;
917 /**************************************************************************
918 * CDROM_Data_GetSerial [internal]
920 static DWORD CDROM_Data_GetSerial(int drive)
922 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
923 WORD offs;
924 union {
925 unsigned long val;
926 unsigned char p[4];
927 } serial;
928 BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
931 if (dev == -1) return 0;
932 offs = CDROM_Data_FindBestVoldesc(dev);
934 serial.val = 0;
935 if (offs)
937 BYTE buf[2048];
938 OSVERSIONINFOA ovi;
939 int i;
941 lseek(dev, offs, SEEK_SET);
942 read(dev, buf, 2048);
944 * OK, another braindead one... argh. Just believe it.
945 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
946 * It's true and nobody will ever be able to change it.
948 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
949 GetVersionExA(&ovi);
950 if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (ovi.dwMajorVersion >= 4))
952 b0 = 3; b1 = 2; b2 = 1; b3 = 0;
954 for (i = 0; i < 2048; i += 4)
956 /* DON'T optimize this into DWORD !! (breaks overflow) */
957 serial.p[b0] += buf[i+b0];
958 serial.p[b1] += buf[i+b1];
959 serial.p[b2] += buf[i+b2];
960 serial.p[b3] += buf[i+b3];
963 close(dev);
964 return serial.val;
967 /**************************************************************************
968 * CDROM_GetSerial [internal]
970 static DWORD CDROM_GetSerial(int drive)
972 DWORD serial = 0;
973 HANDLE h = CDROM_Open(drive);
974 CDROM_DISK_DATA cdd;
975 DWORD br;
977 if (!h || ! !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
978 return 0;
980 switch (cdd.DiskData & 0x03)
982 case CDROM_DISK_DATA_TRACK:
983 /* hopefully a data CD */
984 serial = CDROM_Data_GetSerial(drive);
985 break;
986 case CDROM_DISK_AUDIO_TRACK:
987 /* fall thru */
988 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
989 serial = CDROM_Audio_GetSerial(h);
990 break;
991 case 0:
992 break;
995 if (serial)
996 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
998 CloseHandle(h);
1000 return serial;
1003 /***********************************************************************
1004 * DRIVE_GetSerialNumber
1006 DWORD DRIVE_GetSerialNumber( int drive )
1008 DWORD serial = 0;
1009 char buff[DRIVE_SUPER];
1011 TRACE("drive %d, type = %d\n", drive, DOSDrives[drive].type);
1013 if (!DRIVE_IsValid( drive )) return 0;
1015 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1017 switch(DOSDrives[drive].type)
1019 case DRIVE_REMOVABLE:
1020 case DRIVE_FIXED:
1021 if (DRIVE_ReadSuperblock(drive,(char *) buff))
1022 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
1023 " Maybe not FAT?\n" ,
1024 DOSDrives[drive].device, 'A'+drive);
1025 else
1026 serial = *((DWORD*)(buff+0x27));
1027 break;
1028 case DRIVE_CDROM:
1029 serial = CDROM_GetSerial(drive);
1030 break;
1031 default:
1032 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
1036 return (serial) ? serial : DOSDrives[drive].serial_conf;
1040 /***********************************************************************
1041 * DRIVE_SetSerialNumber
1043 int DRIVE_SetSerialNumber( int drive, DWORD serial )
1045 char buff[DRIVE_SUPER];
1047 if (!DRIVE_IsValid( drive )) return 0;
1049 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1051 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
1052 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
1053 /* check, if the drive has a FAT filesystem */
1054 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
1055 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
1056 return 1;
1059 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
1060 DOSDrives[drive].serial_conf = serial;
1061 return 1;
1065 /***********************************************************************
1066 * DRIVE_GetType
1068 static UINT DRIVE_GetType( int drive )
1070 if (!DRIVE_IsValid( drive )) return DRIVE_NO_ROOT_DIR;
1071 return DOSDrives[drive].type;
1075 /***********************************************************************
1076 * DRIVE_GetFlags
1078 UINT DRIVE_GetFlags( int drive )
1080 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1081 return DOSDrives[drive].flags;
1084 /***********************************************************************
1085 * DRIVE_GetCodepage
1087 UINT DRIVE_GetCodepage( int drive )
1089 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1090 return DOSDrives[drive].codepage;
1094 /***********************************************************************
1095 * DRIVE_Chdir
1097 int DRIVE_Chdir( int drive, LPCWSTR path )
1099 DOS_FULL_NAME full_name;
1100 WCHAR buffer[MAX_PATHNAME_LEN];
1101 LPSTR unix_cwd;
1102 BY_HANDLE_FILE_INFORMATION info;
1103 TDB *pTask = TASK_GetCurrent();
1105 buffer[0] = 'A' + drive;
1106 buffer[1] = ':';
1107 buffer[2] = 0;
1108 TRACE("(%s,%s)\n", debugstr_w(buffer), debugstr_w(path) );
1109 strncpyW( buffer + 2, path, MAX_PATHNAME_LEN - 2 );
1110 buffer[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1112 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
1113 if (!FILE_Stat( full_name.long_name, &info, NULL )) return 0;
1114 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1116 SetLastError( ERROR_FILE_NOT_FOUND );
1117 return 0;
1119 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
1120 while (*unix_cwd == '/') unix_cwd++;
1122 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1123 'A' + drive, unix_cwd, debugstr_w(full_name.short_name + 3) );
1125 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
1126 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
1127 DOSDrives[drive].dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name.short_name) - 2) * sizeof(WCHAR));
1128 strcpyW(DOSDrives[drive].dos_cwd, full_name.short_name + 3);
1129 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
1131 if (pTask && (pTask->curdrive & 0x80) &&
1132 ((pTask->curdrive & ~0x80) == drive))
1134 WideCharToMultiByte(CP_ACP, 0, full_name.short_name + 2, -1,
1135 pTask->curdir, sizeof(pTask->curdir), NULL, NULL);
1136 DRIVE_LastTask = GetCurrentTask();
1138 return 1;
1142 /***********************************************************************
1143 * DRIVE_Disable
1145 int DRIVE_Disable( int drive )
1147 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1149 SetLastError( ERROR_INVALID_DRIVE );
1150 return 0;
1152 DOSDrives[drive].flags |= DRIVE_DISABLED;
1153 return 1;
1157 /***********************************************************************
1158 * DRIVE_Enable
1160 int DRIVE_Enable( int drive )
1162 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1164 SetLastError( ERROR_INVALID_DRIVE );
1165 return 0;
1167 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1168 return 1;
1172 /***********************************************************************
1173 * DRIVE_SetLogicalMapping
1175 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
1177 /* If new_drive is already valid, do nothing and return 0
1178 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1180 DOSDRIVE *old, *new;
1182 old = DOSDrives + existing_drive;
1183 new = DOSDrives + new_drive;
1185 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
1186 !old->root ||
1187 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
1189 SetLastError( ERROR_INVALID_DRIVE );
1190 return 0;
1193 if ( new->root )
1195 TRACE("Can't map drive %c: to already existing drive %c:\n",
1196 'A' + existing_drive, 'A' + new_drive );
1197 /* it is already mapped there, so return success */
1198 if (!strcmp(old->root,new->root))
1199 return 1;
1200 return 0;
1203 new->root = heap_strdup( old->root );
1204 new->dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(old->dos_cwd) + 1) * sizeof(WCHAR));
1205 strcpyW(new->dos_cwd, old->dos_cwd);
1206 new->unix_cwd = heap_strdup( old->unix_cwd );
1207 new->device = heap_strdup( old->device );
1208 memcpy ( new->label_conf, old->label_conf, 12 );
1209 memcpy ( new->label_read, old->label_read, 12 );
1210 new->serial_conf = old->serial_conf;
1211 new->type = old->type;
1212 new->flags = old->flags;
1213 new->dev = old->dev;
1214 new->ino = old->ino;
1216 TRACE("Drive %c: is now equal to drive %c:\n",
1217 'A' + new_drive, 'A' + existing_drive );
1219 return 1;
1223 /***********************************************************************
1224 * DRIVE_OpenDevice
1226 * Open the drive raw device and return a Unix fd (or -1 on error).
1228 int DRIVE_OpenDevice( int drive, int flags )
1230 if (!DRIVE_IsValid( drive )) return -1;
1231 return open( DOSDrives[drive].device, flags );
1235 /***********************************************************************
1236 * DRIVE_RawRead
1238 * Read raw sectors from a device
1240 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1242 int fd;
1244 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1246 lseek( fd, begin * 512, SEEK_SET );
1247 /* FIXME: check errors */
1248 read( fd, dataptr, nr_sect * 512 );
1249 close( fd );
1251 else
1253 memset(dataptr, 0, nr_sect * 512);
1254 if (fake_success)
1256 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
1257 if (begin == 1) *dataptr = 0xf8;
1259 else
1260 return 0;
1262 return 1;
1266 /***********************************************************************
1267 * DRIVE_RawWrite
1269 * Write raw sectors to a device
1271 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1273 int fd;
1275 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1277 lseek( fd, begin * 512, SEEK_SET );
1278 /* FIXME: check errors */
1279 write( fd, dataptr, nr_sect * 512 );
1280 close( fd );
1282 else
1283 if (!(fake_success))
1284 return 0;
1286 return 1;
1290 /***********************************************************************
1291 * DRIVE_GetFreeSpace
1293 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
1294 PULARGE_INTEGER available )
1296 struct statfs info;
1298 if (!DRIVE_IsValid(drive))
1300 SetLastError( ERROR_PATH_NOT_FOUND );
1301 return 0;
1304 /* FIXME: add autoconf check for this */
1305 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1306 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
1307 #else
1308 if (statfs( DOSDrives[drive].root, &info) < 0)
1309 #endif
1311 FILE_SetDosError();
1312 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
1313 return 0;
1316 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
1317 #ifdef HAVE_STRUCT_STATFS_F_BAVAIL
1318 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
1319 #else
1320 # ifdef HAVE_STRUCT_STATFS_F_BFREE
1321 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
1322 # else
1323 # error "statfs has no bfree/bavail member!"
1324 # endif
1325 #endif
1326 if (DOSDrives[drive].type == DRIVE_CDROM)
1327 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1328 available->QuadPart = 0;
1330 return 1;
1333 /***********************************************************************
1334 * DRIVE_GetCurrentDirectory
1335 * Returns "X:\\path\\etc\\".
1337 * Despite the API description, return required length including the
1338 * terminating null when buffer too small. This is the real behaviour.
1340 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPWSTR buf )
1342 UINT ret;
1343 LPCWSTR dos_cwd = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1344 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1346 ret = strlenW(dos_cwd) + 3; /* length of WHOLE current directory */
1347 if (ret >= buflen) return ret + 1;
1349 strcpyW( buf, driveA_rootW );
1350 buf[0] += DRIVE_GetCurrentDrive();
1351 strcatW( buf, dos_cwd );
1352 return ret;
1356 /***********************************************************************
1357 * DRIVE_BuildEnv
1359 * Build the environment array containing the drives' current directories.
1360 * Resulting pointer must be freed with HeapFree.
1362 char *DRIVE_BuildEnv(void)
1364 int i, length = 0;
1365 LPCWSTR cwd[MAX_DOS_DRIVES];
1366 char *env, *p;
1368 for (i = 0; i < MAX_DOS_DRIVES; i++)
1370 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0])
1371 length += WideCharToMultiByte(DRIVE_GetCodepage(i), 0,
1372 cwd[i], -1, NULL, 0, NULL, NULL) + 7;
1374 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
1375 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
1377 if (cwd[i] && cwd[i][0])
1379 *p++ = '='; *p++ = 'A' + i; *p++ = ':';
1380 *p++ = '='; *p++ = 'A' + i; *p++ = ':'; *p++ = '\\';
1381 WideCharToMultiByte(DRIVE_GetCodepage(i), 0, cwd[i], -1, p, 0x7fffffff, NULL, NULL);
1382 p += strlen(p) + 1;
1385 *p = 0;
1386 return env;
1390 /***********************************************************************
1391 * GetDiskFreeSpace (KERNEL.422)
1393 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
1394 LPDWORD sector_bytes, LPDWORD free_clusters,
1395 LPDWORD total_clusters )
1397 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
1398 free_clusters, total_clusters );
1402 /***********************************************************************
1403 * GetDiskFreeSpaceW (KERNEL32.@)
1405 * Fails if expression resulting from current drive's dir and "root"
1406 * is not a root dir of the target drive.
1408 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1409 * if the corresponding info is unneeded.
1411 * FIXME: needs to support UNC names from Win95 OSR2 on.
1413 * Behaviour under Win95a:
1414 * CurrDir root result
1415 * "E:\\TEST" "E:" FALSE
1416 * "E:\\" "E:" TRUE
1417 * "E:\\" "E" FALSE
1418 * "E:\\" "\\" TRUE
1419 * "E:\\TEST" "\\" TRUE
1420 * "E:\\TEST" ":\\" FALSE
1421 * "E:\\TEST" "E:\\" TRUE
1422 * "E:\\TEST" "" FALSE
1423 * "E:\\" "" FALSE (!)
1424 * "E:\\" 0x0 TRUE
1425 * "E:\\TEST" 0x0 TRUE (!)
1426 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1427 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1429 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1430 LPDWORD sector_bytes, LPDWORD free_clusters,
1431 LPDWORD total_clusters )
1433 int drive, sec_size;
1434 ULARGE_INTEGER size,available;
1435 LPCWSTR path;
1436 DWORD cluster_sec;
1438 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root), cluster_sectors, sector_bytes,
1439 free_clusters, total_clusters);
1441 if (!root || root[0] == '\\' || root[0] == '/')
1442 drive = DRIVE_GetCurrentDrive();
1443 else
1444 if (root[0] && root[1] == ':') /* root contains drive tag */
1446 drive = toupperW(root[0]) - 'A';
1447 path = &root[2];
1448 if (path[0] == '\0')
1450 path = DRIVE_GetDosCwd(drive);
1451 if (!path)
1453 SetLastError(ERROR_PATH_NOT_FOUND);
1454 return FALSE;
1457 else
1458 if (path[0] == '\\')
1459 path++;
1461 if (path[0]) /* oops, we are in a subdir */
1463 SetLastError(ERROR_INVALID_NAME);
1464 return FALSE;
1467 else
1469 if (!root[0])
1470 SetLastError(ERROR_PATH_NOT_FOUND);
1471 else
1472 SetLastError(ERROR_INVALID_NAME);
1473 return FALSE;
1476 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1478 /* Cap the size and available at 2GB as per specs. */
1479 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1481 size.s.HighPart = 0;
1482 size.s.LowPart = 0x7fffffff;
1484 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1486 available.s.HighPart =0;
1487 available.s.LowPart = 0x7fffffff;
1489 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1490 size.s.LowPart /= sec_size;
1491 available.s.LowPart /= sec_size;
1492 /* FIXME: probably have to adjust those variables too for CDFS */
1493 cluster_sec = 1;
1494 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1496 if (cluster_sectors)
1497 *cluster_sectors = cluster_sec;
1498 if (sector_bytes)
1499 *sector_bytes = sec_size;
1500 if (free_clusters)
1501 *free_clusters = available.s.LowPart / cluster_sec;
1502 if (total_clusters)
1503 *total_clusters = size.s.LowPart / cluster_sec;
1504 return TRUE;
1508 /***********************************************************************
1509 * GetDiskFreeSpaceA (KERNEL32.@)
1511 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1512 LPDWORD sector_bytes, LPDWORD free_clusters,
1513 LPDWORD total_clusters )
1515 UNICODE_STRING rootW;
1516 BOOL ret = FALSE;
1518 if (root)
1520 if(!RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1522 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1523 return FALSE;
1526 else
1527 rootW.Buffer = NULL;
1529 ret = GetDiskFreeSpaceW(rootW.Buffer, cluster_sectors, sector_bytes,
1530 free_clusters, total_clusters );
1531 RtlFreeUnicodeString(&rootW);
1533 return ret;
1537 /***********************************************************************
1538 * GetDiskFreeSpaceExW (KERNEL32.@)
1540 * This function is used to acquire the size of the available and
1541 * total space on a logical volume.
1543 * RETURNS
1545 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1546 * detailed error information.
1549 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root,
1550 PULARGE_INTEGER avail,
1551 PULARGE_INTEGER total,
1552 PULARGE_INTEGER totalfree)
1554 int drive;
1555 ULARGE_INTEGER size,available;
1557 if (!root) drive = DRIVE_GetCurrentDrive();
1558 else
1559 { /* C: always works for GetDiskFreeSpaceEx */
1560 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
1562 FIXME("there are valid root names which are not supported yet\n");
1563 /* ..like UNC names, for instance. */
1565 WARN("invalid root '%s'\n", debugstr_w(root));
1566 return FALSE;
1568 drive = toupperW(root[0]) - 'A';
1571 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1573 if (total)
1575 total->s.HighPart = size.s.HighPart;
1576 total->s.LowPart = size.s.LowPart;
1579 if (totalfree)
1581 totalfree->s.HighPart = available.s.HighPart;
1582 totalfree->s.LowPart = available.s.LowPart;
1585 if (avail)
1587 if (FIXME_ON(dosfs))
1589 /* On Windows2000, we need to check the disk quota
1590 allocated for the user owning the calling process. We
1591 don't want to be more obtrusive than necessary with the
1592 FIXME messages, so don't print the FIXME unless Wine is
1593 actually masquerading as Windows2000. */
1595 OSVERSIONINFOA ovi;
1596 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1597 if (GetVersionExA(&ovi))
1599 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1600 FIXME("no per-user quota support yet\n");
1604 /* Quick hack, should eventually be fixed to work 100% with
1605 Windows2000 (see comment above). */
1606 avail->s.HighPart = available.s.HighPart;
1607 avail->s.LowPart = available.s.LowPart;
1610 return TRUE;
1613 /***********************************************************************
1614 * GetDiskFreeSpaceExA (KERNEL32.@)
1616 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
1617 PULARGE_INTEGER total,
1618 PULARGE_INTEGER totalfree)
1620 UNICODE_STRING rootW;
1621 BOOL ret;
1623 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
1624 else rootW.Buffer = NULL;
1626 ret = GetDiskFreeSpaceExW( rootW.Buffer, avail, total, totalfree);
1628 RtlFreeUnicodeString(&rootW);
1629 return ret;
1632 /***********************************************************************
1633 * GetDriveType (KERNEL.136)
1634 * This function returns the type of a drive in Win16.
1635 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1636 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1637 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1638 * do any pseudo-clever changes.
1640 * RETURNS
1641 * drivetype DRIVE_xxx
1643 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1645 UINT type = DRIVE_GetType(drive);
1646 TRACE("(%c:)\n", 'A' + drive );
1647 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1648 return type;
1652 /***********************************************************************
1653 * GetDriveTypeW (KERNEL32.@)
1655 * Returns the type of the disk drive specified. If root is NULL the
1656 * root of the current directory is used.
1658 * RETURNS
1660 * Type of drive (from Win32 SDK):
1662 * DRIVE_UNKNOWN unable to find out anything about the drive
1663 * DRIVE_NO_ROOT_DIR nonexistent root dir
1664 * DRIVE_REMOVABLE the disk can be removed from the machine
1665 * DRIVE_FIXED the disk can not be removed from the machine
1666 * DRIVE_REMOTE network disk
1667 * DRIVE_CDROM CDROM drive
1668 * DRIVE_RAMDISK virtual disk in RAM
1670 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1672 int drive;
1673 TRACE("(%s)\n", debugstr_w(root));
1675 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1676 else
1678 if ((root[1]) && (root[1] != ':'))
1680 WARN("invalid root %s\n", debugstr_w(root));
1681 return DRIVE_NO_ROOT_DIR;
1683 drive = toupperW(root[0]) - 'A';
1685 return DRIVE_GetType(drive);
1689 /***********************************************************************
1690 * GetDriveTypeA (KERNEL32.@)
1692 UINT WINAPI GetDriveTypeA( LPCSTR root )
1694 UNICODE_STRING rootW;
1695 UINT ret = 0;
1697 if (root)
1699 if( !RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1701 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1702 return 0;
1705 else
1706 rootW.Buffer = NULL;
1708 ret = GetDriveTypeW(rootW.Buffer);
1710 RtlFreeUnicodeString(&rootW);
1711 return ret;
1716 /***********************************************************************
1717 * GetCurrentDirectory (KERNEL.411)
1719 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1721 WCHAR cur_dirW[MAX_PATH];
1723 DRIVE_GetCurrentDirectory(MAX_PATH, cur_dirW);
1724 return (UINT16)WideCharToMultiByte(CP_ACP, 0, cur_dirW, -1, buf, buflen, NULL, NULL);
1728 /***********************************************************************
1729 * GetCurrentDirectoryW (KERNEL32.@)
1731 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1733 UINT ret;
1734 WCHAR longname[MAX_PATHNAME_LEN];
1735 WCHAR shortname[MAX_PATHNAME_LEN];
1737 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1738 if ( ret > MAX_PATHNAME_LEN ) {
1739 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1740 return ret;
1742 GetLongPathNameW(shortname, longname, MAX_PATHNAME_LEN);
1743 ret = strlenW( longname ) + 1;
1744 if (ret > buflen) return ret;
1745 strcpyW(buf, longname);
1746 return ret - 1;
1749 /***********************************************************************
1750 * GetCurrentDirectoryA (KERNEL32.@)
1752 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1754 WCHAR bufferW[MAX_PATH];
1755 DWORD ret, retW;
1757 retW = GetCurrentDirectoryW(MAX_PATH, bufferW);
1759 if (!retW)
1760 ret = 0;
1761 else if (retW > MAX_PATH)
1763 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1764 ret = 0;
1766 else
1768 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1769 if (buflen >= ret)
1771 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, buflen, NULL, NULL);
1772 ret--; /* length without 0 */
1775 return ret;
1779 /***********************************************************************
1780 * SetCurrentDirectory (KERNEL.412)
1782 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1784 return SetCurrentDirectoryA( dir );
1788 /***********************************************************************
1789 * SetCurrentDirectoryW (KERNEL32.@)
1791 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dir )
1793 int drive, olddrive = DRIVE_GetCurrentDrive();
1795 if (!dir)
1797 SetLastError(ERROR_INVALID_PARAMETER);
1798 return FALSE;
1800 if (dir[0] && (dir[1]==':'))
1802 drive = toupperW( *dir ) - 'A';
1803 dir += 2;
1805 else
1806 drive = olddrive;
1808 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1809 sets pTask->curdir only if pTask->curdrive is drive */
1810 if (!(DRIVE_SetCurrentDrive( drive )))
1811 return FALSE;
1813 /* FIXME: what about empty strings? Add a \\ ? */
1814 if (!DRIVE_Chdir( drive, dir )) {
1815 DRIVE_SetCurrentDrive(olddrive);
1816 return FALSE;
1818 return TRUE;
1822 /***********************************************************************
1823 * SetCurrentDirectoryA (KERNEL32.@)
1825 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1827 UNICODE_STRING dirW;
1828 BOOL ret = FALSE;
1830 if (!dir)
1832 SetLastError(ERROR_INVALID_PARAMETER);
1833 return FALSE;
1836 if (RtlCreateUnicodeStringFromAsciiz(&dirW, dir))
1838 ret = SetCurrentDirectoryW(dirW.Buffer);
1839 RtlFreeUnicodeString(&dirW);
1841 else
1842 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1843 return ret;
1847 /***********************************************************************
1848 * GetLogicalDriveStringsA (KERNEL32.@)
1850 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1852 int drive, count;
1854 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1855 if (DRIVE_IsValid(drive)) count++;
1856 if ((count * 4) + 1 <= len)
1858 LPSTR p = buffer;
1859 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1860 if (DRIVE_IsValid(drive))
1862 *p++ = 'a' + drive;
1863 *p++ = ':';
1864 *p++ = '\\';
1865 *p++ = '\0';
1867 *p = '\0';
1868 return count * 4;
1870 else
1871 return (count * 4) + 1; /* account for terminating null */
1872 /* The API tells about these different return values */
1876 /***********************************************************************
1877 * GetLogicalDriveStringsW (KERNEL32.@)
1879 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1881 int drive, count;
1883 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1884 if (DRIVE_IsValid(drive)) count++;
1885 if (count * 4 * sizeof(WCHAR) <= len)
1887 LPWSTR p = buffer;
1888 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1889 if (DRIVE_IsValid(drive))
1891 *p++ = (WCHAR)('a' + drive);
1892 *p++ = (WCHAR)':';
1893 *p++ = (WCHAR)'\\';
1894 *p++ = (WCHAR)'\0';
1896 *p = (WCHAR)'\0';
1898 return count * 4 * sizeof(WCHAR);
1902 /***********************************************************************
1903 * GetLogicalDrives (KERNEL32.@)
1905 DWORD WINAPI GetLogicalDrives(void)
1907 DWORD ret = 0;
1908 int drive;
1910 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1912 if ( (DRIVE_IsValid(drive)) ||
1913 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1914 ret |= (1 << drive);
1916 return ret;
1920 /***********************************************************************
1921 * GetVolumeInformationW (KERNEL32.@)
1923 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1924 DWORD label_len, DWORD *serial,
1925 DWORD *filename_len, DWORD *flags,
1926 LPWSTR fsname, DWORD fsname_len )
1928 int drive;
1929 LPWSTR cp;
1931 /* FIXME, SetLastError()s missing */
1933 if (!root) drive = DRIVE_GetCurrentDrive();
1934 else
1936 if (root[0] && root[1] != ':')
1938 WARN("invalid root %s\n", debugstr_w(root));
1939 return FALSE;
1941 drive = toupperW(root[0]) - 'A';
1943 if (!DRIVE_IsValid( drive )) return FALSE;
1944 if (label && label_len)
1946 strncpyW( label, DRIVE_GetLabel(drive), label_len );
1947 label[label_len - 1] = 0; /* ensure 0 termination */
1948 cp = label + strlenW(label);
1949 while (cp != label && *(cp-1) == ' ') cp--;
1950 *cp = '\0';
1952 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1954 /* Set the filesystem information */
1955 /* Note: we only emulate a FAT fs at present */
1957 if (filename_len) {
1958 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1959 *filename_len = 12;
1960 else
1961 *filename_len = 255;
1963 if (flags)
1965 *flags=0;
1966 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1967 *flags|=FS_CASE_SENSITIVE;
1968 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1969 *flags|=FS_CASE_IS_PRESERVED;
1971 if (fsname && fsname_len)
1973 /* Diablo checks that return code ... */
1974 if (DOSDrives[drive].type == DRIVE_CDROM)
1976 static const WCHAR cdfsW[] = {'C','D','F','S',0};
1977 strncpyW( fsname, cdfsW, fsname_len );
1979 else
1981 static const WCHAR fatW[] = {'F','A','T',0};
1982 strncpyW( fsname, fatW, fsname_len );
1984 fsname[fsname_len - 1] = 0; /* ensure 0 termination */
1986 return TRUE;
1990 /***********************************************************************
1991 * GetVolumeInformationA (KERNEL32.@)
1993 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1994 DWORD label_len, DWORD *serial,
1995 DWORD *filename_len, DWORD *flags,
1996 LPSTR fsname, DWORD fsname_len )
1998 UNICODE_STRING rootW;
1999 LPWSTR labelW, fsnameW;
2000 BOOL ret;
2002 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2003 else rootW.Buffer = NULL;
2004 labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
2005 fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
2007 if ((ret = GetVolumeInformationW(rootW.Buffer, labelW, label_len, serial,
2008 filename_len, flags, fsnameW, fsname_len)))
2010 if (label) WideCharToMultiByte(CP_ACP, 0, labelW, -1, label, label_len, NULL, NULL);
2011 if (fsname) WideCharToMultiByte(CP_ACP, 0, fsnameW, -1, fsname, fsname_len, NULL, NULL);
2014 RtlFreeUnicodeString(&rootW);
2015 if (labelW) HeapFree( GetProcessHeap(), 0, labelW );
2016 if (fsnameW) HeapFree( GetProcessHeap(), 0, fsnameW );
2017 return ret;
2020 /***********************************************************************
2021 * SetVolumeLabelW (KERNEL32.@)
2023 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR volname )
2025 int drive;
2027 /* FIXME, SetLastErrors missing */
2029 if (!root) drive = DRIVE_GetCurrentDrive();
2030 else
2032 if ((root[1]) && (root[1] != ':'))
2034 WARN("invalid root %s\n", debugstr_w(root));
2035 return FALSE;
2037 drive = toupperW(root[0]) - 'A';
2039 if (!DRIVE_IsValid( drive )) return FALSE;
2041 /* some copy protection stuff check this */
2042 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
2044 strncpyW(DOSDrives[drive].label_conf, volname, 12);
2045 DOSDrives[drive].label_conf[12 - 1] = 0; /* ensure 0 termination */
2046 return TRUE;
2049 /***********************************************************************
2050 * SetVolumeLabelA (KERNEL32.@)
2052 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
2054 UNICODE_STRING rootW, volnameW;
2055 BOOL ret;
2057 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2058 else rootW.Buffer = NULL;
2059 if (volname) RtlCreateUnicodeStringFromAsciiz(&volnameW, volname);
2060 else volnameW.Buffer = NULL;
2062 ret = SetVolumeLabelW( rootW.Buffer, volnameW.Buffer );
2064 RtlFreeUnicodeString(&rootW);
2065 RtlFreeUnicodeString(&volnameW);
2066 return ret;
2069 /***********************************************************************
2070 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
2072 DWORD WINAPI GetVolumeNameForVolumeMountPointW(LPWSTR str, DWORD a, DWORD b)
2074 FIXME("(%s, %lx, %lx): stub\n", debugstr_w(str), a, b);
2075 return 0;