int33 21h is identical to int33 00h.
[wine.git] / files / drive.c
blob1b300442611d6abc0e49a138c2b75a28d9b23c45
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 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
143 extern void CDROM_InitRegistry(int dev);
145 /***********************************************************************
146 * DRIVE_GetDriveType
148 static inline UINT DRIVE_GetDriveType( INT drive, LPCWSTR value )
150 int i;
152 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
154 if (!strcmpiW( value, DRIVE_Types[i] )) return i;
156 MESSAGE("Drive %c: unknown drive type %s, defaulting to 'hd'.\n",
157 'A' + drive, debugstr_w(value) );
158 return DRIVE_FIXED;
162 /***********************************************************************
163 * DRIVE_GetFSFlags
165 static UINT DRIVE_GetFSFlags( INT drive, LPCWSTR value )
167 const FS_DESCR *descr;
169 for (descr = DRIVE_Filesystems; *descr->name; descr++)
170 if (!strcmpiW( value, descr->name )) return descr->flags;
171 MESSAGE("Drive %c: unknown filesystem type %s, defaulting to 'win95'.\n",
172 'A' + drive, debugstr_w(value) );
173 return DRIVE_CASE_PRESERVING;
177 /***********************************************************************
178 * DRIVE_Init
180 int DRIVE_Init(void)
182 int i, len, count = 0;
183 WCHAR driveW[] = {'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
184 'W','i','n','e','\\','W','i','n','e','\\',
185 'C','o','n','f','i','g','\\','D','r','i','v','e',' ','A',0};
186 WCHAR drive_env[] = {'=','A',':',0};
187 WCHAR path[MAX_PATHNAME_LEN];
188 char tmp[MAX_PATHNAME_LEN*sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
189 struct stat drive_stat_buffer;
190 WCHAR *p;
191 DOSDRIVE *drive;
192 HKEY hkey;
193 DWORD dummy;
194 OBJECT_ATTRIBUTES attr;
195 UNICODE_STRING nameW;
197 static const WCHAR PathW[] = {'P','a','t','h',0};
198 static const WCHAR CodepageW[] = {'C','o','d','e','p','a','g','e',0};
199 static const WCHAR LabelW[] = {'L','a','b','e','l',0};
200 static const WCHAR SerialW[] = {'S','e','r','i','a','l',0};
201 static const WCHAR TypeW[] = {'T','y','p','e',0};
202 static const WCHAR FilesystemW[] = {'F','i','l','e','s','y','s','t','e','m',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};
209 attr.Length = sizeof(attr);
210 attr.RootDirectory = 0;
211 attr.ObjectName = &nameW;
212 attr.Attributes = 0;
213 attr.SecurityDescriptor = NULL;
214 attr.SecurityQualityOfService = NULL;
216 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
218 RtlInitUnicodeString( &nameW, driveW );
219 nameW.Buffer[(nameW.Length / sizeof(WCHAR)) - 1] = 'A' + i;
220 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ) != STATUS_SUCCESS) continue;
222 /* Get the code page number */
223 RtlInitUnicodeString( &nameW, CodepageW );
224 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
226 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
227 drive->codepage = strtolW( data, NULL, 10 );
230 /* Get the root path */
231 RtlInitUnicodeString( &nameW, PathW );
232 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
234 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
235 ExpandEnvironmentStringsW( data, path, sizeof(path)/sizeof(WCHAR) );
237 p = path + strlenW(path) - 1;
238 while ((p > path) && (*p == '/')) *p-- = '\0';
240 if (path[0] == '/')
242 len = WideCharToMultiByte(drive->codepage, 0, path, -1, NULL, 0, NULL, NULL);
243 drive->root = HeapAlloc(GetProcessHeap(), 0, len);
244 WideCharToMultiByte(drive->codepage, 0, path, -1, drive->root, len, NULL, NULL);
246 else
248 /* relative paths are relative to config dir */
249 const char *config = wine_get_config_dir();
250 len = strlen(config);
251 len += WideCharToMultiByte(drive->codepage, 0, path, -1, NULL, 0, NULL, NULL) + 2;
252 drive->root = HeapAlloc( GetProcessHeap(), 0, len );
253 len -= sprintf( drive->root, "%s/", config );
254 WideCharToMultiByte(drive->codepage, 0, path, -1,
255 drive->root + strlen(drive->root), len, NULL, NULL);
258 if (stat( drive->root, &drive_stat_buffer ))
260 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
261 drive->root, strerror(errno), 'A' + i);
262 HeapFree( GetProcessHeap(), 0, drive->root );
263 drive->root = NULL;
264 goto next;
266 if (!S_ISDIR(drive_stat_buffer.st_mode))
268 MESSAGE("%s is not a directory, ignoring drive %c:\n",
269 drive->root, 'A' + i );
270 HeapFree( GetProcessHeap(), 0, drive->root );
271 drive->root = NULL;
272 goto next;
275 drive->dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(drive->dos_cwd[0]));
276 drive->unix_cwd = heap_strdup( "" );
277 drive->device = NULL;
278 drive->flags = 0;
279 drive->dev = drive_stat_buffer.st_dev;
280 drive->ino = drive_stat_buffer.st_ino;
282 /* Get the drive type */
283 RtlInitUnicodeString( &nameW, TypeW );
284 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
286 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
287 drive->type = DRIVE_GetDriveType( i, data );
289 else drive->type = DRIVE_FIXED;
291 /* Get the drive label */
292 RtlInitUnicodeString( &nameW, LabelW );
293 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
295 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
296 lstrcpynW( drive->label_conf, data, 12 );
298 if ((len = strlenW(drive->label_conf)) < 11)
300 /* Pad label with spaces */
301 while(len < 11) drive->label_conf[len++] = ' ';
302 drive->label_conf[11] = '\0';
305 /* Get the serial number */
306 RtlInitUnicodeString( &nameW, SerialW );
307 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
309 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
310 drive->serial_conf = strtoulW( data, NULL, 16 );
312 else drive->serial_conf = 12345678;
314 /* Get the filesystem type */
315 RtlInitUnicodeString( &nameW, FilesystemW );
316 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
318 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
319 drive->flags = DRIVE_GetFSFlags( i, data );
321 else drive->flags = DRIVE_CASE_PRESERVING;
323 /* Get the device */
324 RtlInitUnicodeString( &nameW, DeviceW );
325 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
327 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
328 len = WideCharToMultiByte(CP_ACP, 0, data, -1, NULL, 0, NULL, NULL);
329 drive->device = HeapAlloc(GetProcessHeap(), 0, len);
330 WideCharToMultiByte(drive->codepage, 0, data, -1, drive->device, len, NULL, NULL);
332 RtlInitUnicodeString( &nameW, ReadVolInfoW );
333 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
335 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
336 if (IS_OPTION_TRUE(data[0])) drive->flags |= DRIVE_READ_VOL_INFO;
338 else drive->flags |= DRIVE_READ_VOL_INFO;
340 if (drive->type == DRIVE_CDROM)
342 int cd_fd;
343 if ((cd_fd = open(drive->device, O_RDONLY|O_NONBLOCK)) != -1)
345 CDROM_InitRegistry(cd_fd);
346 close(cd_fd);
351 /* Get the FailReadOnly flag */
352 RtlInitUnicodeString( &nameW, FailReadOnlyW );
353 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
355 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
356 if (IS_OPTION_TRUE(data[0])) drive->flags |= DRIVE_FAIL_READ_ONLY;
359 /* Make the first hard disk the current drive */
360 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
361 DRIVE_CurDrive = i;
363 count++;
364 TRACE("Drive %c: path=%s type=%s label=%s serial=%08lx "
365 "flags=%08x codepage=%u dev=%x ino=%x\n",
366 'A' + i, drive->root, debugstr_w(DRIVE_Types[drive->type]),
367 debugstr_w(drive->label_conf), drive->serial_conf, drive->flags,
368 drive->codepage, (int)drive->dev, (int)drive->ino );
371 next:
372 NtClose( hkey );
375 if (!count)
377 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
378 /* Create a C drive pointing to Unix root dir */
379 DOSDrives[2].root = heap_strdup( "/" );
380 DOSDrives[2].dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DOSDrives[2].dos_cwd[0]));
381 DOSDrives[2].unix_cwd = heap_strdup( "" );
382 strcpyW( DOSDrives[2].label_conf, driveC_labelW );
383 DOSDrives[2].serial_conf = 12345678;
384 DOSDrives[2].type = DRIVE_FIXED;
385 DOSDrives[2].device = NULL;
386 DOSDrives[2].flags = 0;
387 DRIVE_CurDrive = 2;
390 /* Make sure the current drive is valid */
391 if (DRIVE_CurDrive == -1)
393 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
395 if (drive->root && !(drive->flags & DRIVE_DISABLED))
397 DRIVE_CurDrive = i;
398 break;
403 /* get current working directory info for all drives */
404 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
406 if (!GetEnvironmentVariableW(drive_env, path, MAX_PATHNAME_LEN)) continue;
407 /* sanity check */
408 if (toupperW(path[0]) != drive_env[1] || path[1] != ':') continue;
409 DRIVE_Chdir( i, path + 2 );
411 return 1;
415 /***********************************************************************
416 * DRIVE_IsValid
418 int DRIVE_IsValid( int drive )
420 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
421 return (DOSDrives[drive].root &&
422 !(DOSDrives[drive].flags & DRIVE_DISABLED));
426 /***********************************************************************
427 * DRIVE_GetCurrentDrive
429 int DRIVE_GetCurrentDrive(void)
431 TDB *pTask = GlobalLock16(GetCurrentTask());
432 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
433 return DRIVE_CurDrive;
437 /***********************************************************************
438 * DRIVE_SetCurrentDrive
440 int DRIVE_SetCurrentDrive( int drive )
442 TDB *pTask = GlobalLock16(GetCurrentTask());
443 if (!DRIVE_IsValid( drive ))
445 SetLastError( ERROR_INVALID_DRIVE );
446 return 0;
448 TRACE("%c:\n", 'A' + drive );
449 DRIVE_CurDrive = drive;
450 if (pTask) pTask->curdrive = drive | 0x80;
451 return 1;
455 /***********************************************************************
456 * DRIVE_FindDriveRoot
458 * Find a drive for which the root matches the beginning of the given path.
459 * This can be used to translate a Unix path into a drive + DOS path.
460 * Return value is the drive, or -1 on error. On success, path is modified
461 * to point to the beginning of the DOS path.
463 * Note: path must be in the encoding of the underlying Unix file system.
465 int DRIVE_FindDriveRoot( const char **path )
467 /* Starting with the full path, check if the device and inode match any of
468 * the wine 'drives'. If not then remove the last path component and try
469 * again. If the last component was a '..' then skip a normal component
470 * since it's a directory that's ascended back out of.
472 int drive, level, len;
473 char buffer[MAX_PATHNAME_LEN];
474 char *p;
475 struct stat st;
477 strcpy( buffer, *path );
478 while ((p = strchr( buffer, '\\' )) != NULL)
479 *p = '/';
480 len = strlen(buffer);
482 /* strip off trailing slashes */
483 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
485 for (;;)
487 /* Find the drive */
488 if (stat( buffer, &st ) == 0 && S_ISDIR( st.st_mode ))
490 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
492 if (!DOSDrives[drive].root ||
493 (DOSDrives[drive].flags & DRIVE_DISABLED))
494 continue;
496 if ((DOSDrives[drive].dev == st.st_dev) &&
497 (DOSDrives[drive].ino == st.st_ino))
499 if (len == 1) len = 0; /* preserve root slash in returned path */
500 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
501 *path, 'A' + drive, buffer, *path + len);
502 *path += len;
503 if (!**path) *path = "\\";
504 return drive;
508 if (len <= 1) return -1; /* reached root */
510 level = 0;
511 while (level < 1)
513 /* find start of the last path component */
514 while (len > 1 && buffer[len - 1] != '/') len--;
515 if (!buffer[len]) break; /* empty component -> reached root */
516 /* does removing it take us up a level? */
517 if (strcmp( buffer + len, "." ) != 0)
518 level += strcmp( buffer + len, ".." ) ? 1 : -1;
519 buffer[len] = 0;
520 /* strip off trailing slashes */
521 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
527 /***********************************************************************
528 * DRIVE_FindDriveRootW
530 * Unicode version of DRIVE_FindDriveRoot.
532 int DRIVE_FindDriveRootW( LPCWSTR *path )
534 int drive, level, len;
535 WCHAR buffer[MAX_PATHNAME_LEN];
536 WCHAR *p;
537 struct stat st;
539 strcpyW( buffer, *path );
540 while ((p = strchrW( buffer, '\\' )) != NULL)
541 *p = '/';
542 len = strlenW(buffer);
544 /* strip off trailing slashes */
545 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
547 for (;;)
549 int codepage = -1;
551 /* Find the drive */
552 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
554 char buffA[MAX_PATHNAME_LEN];
556 if (!DOSDrives[drive].root ||
557 (DOSDrives[drive].flags & DRIVE_DISABLED))
558 continue;
560 if (codepage != DOSDrives[drive].codepage)
562 WideCharToMultiByte( DOSDrives[drive].codepage, 0, buffer, -1,
563 buffA, sizeof(buffA), NULL, NULL );
564 if (stat( buffA, &st ) == -1 || !S_ISDIR( st.st_mode ))
566 codepage = -1;
567 continue;
569 codepage = DOSDrives[drive].codepage;
572 if ((DOSDrives[drive].dev == st.st_dev) &&
573 (DOSDrives[drive].ino == st.st_ino))
575 static const WCHAR rootW[] = {'\\',0};
577 if (len == 1) len = 0; /* preserve root slash in returned path */
578 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
579 debugstr_w(*path), 'A' + drive, debugstr_w(buffer), debugstr_w(*path + len));
580 *path += len;
581 if (!**path) *path = rootW;
582 return drive;
585 if (len <= 1) return -1; /* reached root */
587 level = 0;
588 while (level < 1)
590 static const WCHAR dotW[] = {'.',0};
591 static const WCHAR dotdotW[] = {'.','.',0};
593 /* find start of the last path component */
594 while (len > 1 && buffer[len - 1] != '/') len--;
595 if (!buffer[len]) break; /* empty component -> reached root */
596 /* does removing it take us up a level? */
597 if (strcmpW( buffer + len, dotW ) != 0)
598 level += strcmpW( buffer + len, dotdotW ) ? 1 : -1;
599 buffer[len] = 0;
600 /* strip off trailing slashes */
601 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
607 /***********************************************************************
608 * DRIVE_GetRoot
610 const char * DRIVE_GetRoot( int drive )
612 if (!DRIVE_IsValid( drive )) return NULL;
613 return DOSDrives[drive].root;
617 /***********************************************************************
618 * DRIVE_GetDosCwd
620 LPCWSTR DRIVE_GetDosCwd( int drive )
622 TDB *pTask = GlobalLock16(GetCurrentTask());
623 if (!DRIVE_IsValid( drive )) return NULL;
625 /* Check if we need to change the directory to the new task. */
626 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
627 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
628 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
630 static const WCHAR rootW[] = {'\\',0};
631 WCHAR curdirW[MAX_PATH];
632 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
633 /* Perform the task-switch */
634 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
635 DRIVE_LastTask = GetCurrentTask();
637 return DOSDrives[drive].dos_cwd;
641 /***********************************************************************
642 * DRIVE_GetUnixCwd
644 const char * DRIVE_GetUnixCwd( int drive )
646 TDB *pTask = GlobalLock16(GetCurrentTask());
647 if (!DRIVE_IsValid( drive )) return NULL;
649 /* Check if we need to change the directory to the new task. */
650 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
651 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
652 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
654 static const WCHAR rootW[] = {'\\',0};
655 WCHAR curdirW[MAX_PATH];
656 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
657 /* Perform the task-switch */
658 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
659 DRIVE_LastTask = GetCurrentTask();
661 return DOSDrives[drive].unix_cwd;
665 /***********************************************************************
666 * DRIVE_GetDevice
668 const char * DRIVE_GetDevice( int drive )
670 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
673 /******************************************************************
674 * static WORD CDROM_Data_FindBestVoldesc
678 static WORD CDROM_Data_FindBestVoldesc(int fd)
680 BYTE cur_vd_type, max_vd_type = 0;
681 unsigned int offs, best_offs = 0, extra_offs = 0;
682 char sig[3];
684 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
686 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
687 * the volume label is displaced forward by 8
689 lseek(fd, offs + 11, SEEK_SET); /* check for non-ISO9660 signature */
690 read(fd, &sig, 3);
691 if ((sig[0] == 'R') && (sig[1] == 'O') && (sig[2]=='M'))
693 extra_offs = 8;
695 lseek(fd, offs + extra_offs, SEEK_SET);
696 read(fd, &cur_vd_type, 1);
697 if (cur_vd_type == 0xff) /* voldesc set terminator */
698 break;
699 if (cur_vd_type > max_vd_type)
701 max_vd_type = cur_vd_type;
702 best_offs = offs + extra_offs;
705 return best_offs;
708 /***********************************************************************
709 * DRIVE_ReadSuperblock
711 * NOTE
712 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
713 * to check, that they are writing on a FAT filesystem !
715 int DRIVE_ReadSuperblock (int drive, char * buff)
717 #define DRIVE_SUPER 96
718 int fd;
719 off_t offs;
720 int ret = 0;
721 struct stat stat_buf;
723 memset(buff, 0, DRIVE_SUPER);
724 /* O_NONBLOCK in case we're opening FIFO; will be reset later */
725 if ((fd = open(DOSDrives[drive].device, O_RDONLY|O_NOCTTY|O_NONBLOCK)) != -1) {
726 if (fstat(fd, &stat_buf) < 0) { /* shouldn't happen since we just opened that file */
727 ERR("fstat() failed for opened device '%s' (drive %c:) ! IT SHOULDN'T HAPPEN !!!\n",
728 DOSDrives[drive].device, 'A'+drive);
729 ret = -1;
730 } else if (!S_ISBLK(stat_buf.st_mode)) {
731 ERR("Device '%s' (drive %c:) is not a block device - check your config\n",
732 DOSDrives[drive].device, 'A'+drive);
733 ret = -1;
734 /* reset O_NONBLOCK */
735 } else if (fcntl(fd, F_SETFL, 0) < 0 || fcntl(fd, F_GETFL) & O_NONBLOCK) {
736 ERR("fcntl() failed to reset O_NONBLOCK for device '%s' (drive %c:)\n",
737 DOSDrives[drive].device, 'A'+drive);
738 ret = -1;
740 if (ret) {
741 close(fd);
742 fd = -1;
744 } else {
745 if (!DOSDrives[drive].device)
746 ERR("No device configured for drive %c: !\n", 'A'+drive);
747 else
748 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
749 (stat(DOSDrives[drive].device, &stat_buf)) ?
750 "not available or symlink not valid ?" : "no permission");
752 if (fd == -1) {
753 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
754 return -1;
757 switch(DOSDrives[drive].type)
759 case DRIVE_REMOVABLE:
760 case DRIVE_FIXED:
761 offs = 0;
762 break;
763 case DRIVE_CDROM:
764 offs = CDROM_Data_FindBestVoldesc(fd);
765 break;
766 default:
767 offs = 0;
768 break;
771 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs))
773 ret = -4;
774 goto the_end;
776 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER)
778 ret = -2;
779 goto the_end;
782 switch(DOSDrives[drive].type)
784 case DRIVE_REMOVABLE:
785 case DRIVE_FIXED:
786 if ((buff[0x26]!=0x29) || /* Check for FAT present */
787 /* FIXME: do really all FAT have their name beginning with
788 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
789 memcmp( buff+0x36,"FAT",3))
791 ERR("The filesystem is not FAT !! (device=%s)\n",
792 DOSDrives[drive].device);
793 ret = -3;
794 goto the_end;
796 break;
797 case DRIVE_CDROM:
798 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
800 ret = -3;
801 goto the_end;
803 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
804 break;
805 default:
806 ret = -3;
807 goto the_end;
810 return close(fd);
811 the_end:
812 close(fd);
813 return ret;
817 /***********************************************************************
818 * DRIVE_WriteSuperblockEntry
820 * NOTE
821 * We are writing as little as possible (ie. not the whole SuperBlock)
822 * not to interfere with kernel. The drive can be mounted !
824 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
826 int fd;
828 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
830 ERR("Cannot open the device %s (for writing)\n",
831 DOSDrives[drive].device);
832 return -1;
834 if (lseek(fd,ofs,SEEK_SET)!=ofs)
836 ERR("lseek failed on device %s !\n",
837 DOSDrives[drive].device);
838 close(fd);
839 return -2;
841 if (write(fd,buff,len)!=len)
843 close(fd);
844 ERR("Cannot write on %s !\n",
845 DOSDrives[drive].device);
846 return -3;
848 return close (fd);
851 /******************************************************************
852 * static HANDLE CDROM_Open
856 static HANDLE CDROM_Open(int drive)
858 WCHAR root[] = {'\\','\\','.','\\','A',':',0};
859 root[4] += drive;
860 return CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
863 /**************************************************************************
864 * CDROM_Data_GetLabel [internal]
866 DWORD CDROM_Data_GetLabel(int drive, WCHAR *label)
868 #define LABEL_LEN 32+1
869 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
870 WORD offs = CDROM_Data_FindBestVoldesc(dev);
871 WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
872 DWORD unicode_id = 0;
874 if (offs)
876 if ((lseek(dev, offs+0x58, SEEK_SET) == offs+0x58)
877 && (read(dev, &unicode_id, 3) == 3))
879 int ver = (unicode_id & 0xff0000) >> 16;
881 if ((lseek(dev, offs+0x28, SEEK_SET) != offs+0x28)
882 || (read(dev, &label_read, LABEL_LEN) != LABEL_LEN))
883 goto failure;
885 close(dev);
886 if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
887 && ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
888 { /* yippee, unicode */
889 int i;
890 WORD ch;
891 for (i=0; i<LABEL_LEN;i++)
892 { /* Motorola -> Intel Unicode conversion :-\ */
893 ch = label_read[i];
894 label_read[i] = (ch << 8) | (ch >> 8);
896 strncpyW(label, label_read, 11);
897 label[11] = 0;
899 else
901 MultiByteToWideChar(DOSDrives[drive].codepage, 0, (LPSTR)label_read, -1, label, 11);
902 label[11] = '\0';
904 return 1;
907 failure:
908 close(dev);
909 ERR("error reading label !\n");
910 return 0;
913 /**************************************************************************
914 * CDROM_GetLabel [internal]
916 static DWORD CDROM_GetLabel(int drive, WCHAR *label)
918 HANDLE h;
919 CDROM_DISK_DATA cdd;
920 DWORD br, ret = 1;
921 BOOL r;
923 h = CDROM_Open(drive);
924 if( !h )
925 return 0;
926 r = DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL,
927 0, &cdd, sizeof(cdd), &br, 0);
928 CloseHandle( h );
929 if( !r )
930 return 0;
932 switch (cdd.DiskData & 0x03)
934 case CDROM_DISK_DATA_TRACK:
935 if (!CDROM_Data_GetLabel(drive, label))
936 ret = 0;
937 break;
938 case CDROM_DISK_AUDIO_TRACK:
940 static const WCHAR audioCD[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
941 strcpyW(label, audioCD);
942 break;
944 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
945 FIXME("Need to get the label of a mixed mode CD!\n");
946 /* This assumes that the first track is a data track! */
947 /* I guess the correct way would be to enumerate all data tracks
948 and check each for the title */
949 if (!CDROM_Data_GetLabel(drive, label))
950 ret = 0;
951 break;
952 case 0:
953 ret = 0;
954 break;
956 TRACE("CD: label is %s\n", debugstr_w(label));
958 return ret;
960 /***********************************************************************
961 * DRIVE_GetLabel
963 LPCWSTR DRIVE_GetLabel( int drive )
965 int read = 0;
966 char buff[DRIVE_SUPER];
967 int offs = -1;
969 if (!DRIVE_IsValid( drive )) return NULL;
970 if (DOSDrives[drive].type == DRIVE_CDROM)
972 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
974 else
975 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
977 if (DRIVE_ReadSuperblock(drive,(char *) buff))
978 ERR("Invalid or unreadable superblock on %s (%c:).\n",
979 DOSDrives[drive].device, (char)(drive+'A'));
980 else {
981 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
982 DOSDrives[drive].type == DRIVE_FIXED)
983 offs = 0x2b;
985 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
986 if (offs != -1)
987 MultiByteToWideChar(DOSDrives[drive].codepage, 0, buff+offs, 11,
988 DOSDrives[drive].label_read, 11);
989 DOSDrives[drive].label_read[11]='\0';
990 read = 1;
994 return (read) ?
995 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
998 #define CDFRAMES_PERSEC 75
999 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
1000 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
1001 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
1003 /**************************************************************************
1004 * CDROM_Audio_GetSerial [internal]
1006 static DWORD CDROM_Audio_GetSerial(HANDLE h)
1008 unsigned long serial = 0;
1009 int i;
1010 WORD wMagic;
1011 DWORD dwStart, dwEnd, br;
1012 CDROM_TOC toc;
1014 if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
1015 return 0;
1018 * wMagic collects the wFrames from track 1
1019 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
1020 * frames.
1021 * There it is collected for correcting the serial when there are less than
1022 * 3 tracks.
1024 wMagic = toc.TrackData[0].Address[2];
1025 dwStart = FRAME_OF_TOC(toc, toc.FirstTrack);
1027 for (i = 0; i <= toc.LastTrack - toc.FirstTrack; i++) {
1028 serial += (toc.TrackData[i].Address[0] << 16) |
1029 (toc.TrackData[i].Address[1] << 8) | toc.TrackData[i].Address[2];
1031 dwEnd = FRAME_OF_TOC(toc, toc.LastTrack + 1);
1033 if (toc.LastTrack - toc.FirstTrack + 1 < 3)
1034 serial += wMagic + (dwEnd - dwStart);
1036 return serial;
1039 /**************************************************************************
1040 * CDROM_Data_GetSerial [internal]
1042 static DWORD CDROM_Data_GetSerial(int drive)
1044 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
1045 WORD offs;
1046 union {
1047 unsigned long val;
1048 unsigned char p[4];
1049 } serial;
1050 BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
1053 if (dev == -1) return 0;
1054 offs = CDROM_Data_FindBestVoldesc(dev);
1056 serial.val = 0;
1057 if (offs)
1059 BYTE buf[2048];
1060 OSVERSIONINFOA ovi;
1061 int i;
1063 lseek(dev, offs, SEEK_SET);
1064 read(dev, buf, 2048);
1066 * OK, another braindead one... argh. Just believe it.
1067 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
1068 * It's true and nobody will ever be able to change it.
1070 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1071 GetVersionExA(&ovi);
1072 if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (ovi.dwMajorVersion >= 4))
1074 b0 = 3; b1 = 2; b2 = 1; b3 = 0;
1076 for (i = 0; i < 2048; i += 4)
1078 /* DON'T optimize this into DWORD !! (breaks overflow) */
1079 serial.p[b0] += buf[i+b0];
1080 serial.p[b1] += buf[i+b1];
1081 serial.p[b2] += buf[i+b2];
1082 serial.p[b3] += buf[i+b3];
1085 close(dev);
1086 return serial.val;
1089 /**************************************************************************
1090 * CDROM_GetSerial [internal]
1092 static DWORD CDROM_GetSerial(int drive)
1094 DWORD serial = 0;
1095 HANDLE h;
1096 CDROM_DISK_DATA cdd;
1097 DWORD br;
1098 BOOL r;
1100 TRACE("%d\n", drive);
1102 h = CDROM_Open(drive);
1103 if( !h )
1104 return 0;
1105 r = DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL,
1106 0, &cdd, sizeof(cdd), &br, 0);
1107 if (!r)
1109 CloseHandle(h);
1110 return 0;
1113 switch (cdd.DiskData & 0x03)
1115 case CDROM_DISK_DATA_TRACK:
1116 /* hopefully a data CD */
1117 serial = CDROM_Data_GetSerial(drive);
1118 break;
1119 case CDROM_DISK_AUDIO_TRACK:
1120 /* fall thru */
1121 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
1122 serial = CDROM_Audio_GetSerial(h);
1123 break;
1124 case 0:
1125 break;
1128 if (serial)
1129 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
1131 CloseHandle(h);
1133 return serial;
1136 /***********************************************************************
1137 * DRIVE_GetSerialNumber
1139 DWORD DRIVE_GetSerialNumber( int drive )
1141 DWORD serial = 0;
1142 char buff[DRIVE_SUPER];
1144 TRACE("drive %d, type = %d\n", drive, DOSDrives[drive].type);
1146 if (!DRIVE_IsValid( drive )) return 0;
1148 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1150 switch(DOSDrives[drive].type)
1152 case DRIVE_REMOVABLE:
1153 case DRIVE_FIXED:
1154 if (DRIVE_ReadSuperblock(drive,(char *) buff))
1155 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
1156 " Maybe not FAT?\n" ,
1157 DOSDrives[drive].device, 'A'+drive);
1158 else
1159 serial = *((DWORD*)(buff+0x27));
1160 break;
1161 case DRIVE_CDROM:
1162 serial = CDROM_GetSerial(drive);
1163 break;
1164 default:
1165 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
1169 return (serial) ? serial : DOSDrives[drive].serial_conf;
1173 /***********************************************************************
1174 * DRIVE_SetSerialNumber
1176 int DRIVE_SetSerialNumber( int drive, DWORD serial )
1178 char buff[DRIVE_SUPER];
1180 if (!DRIVE_IsValid( drive )) return 0;
1182 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1184 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
1185 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
1186 /* check, if the drive has a FAT filesystem */
1187 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
1188 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
1189 return 1;
1192 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
1193 DOSDrives[drive].serial_conf = serial;
1194 return 1;
1198 /***********************************************************************
1199 * DRIVE_GetType
1201 static UINT DRIVE_GetType( int drive )
1203 if (!DRIVE_IsValid( drive )) return DRIVE_NO_ROOT_DIR;
1204 return DOSDrives[drive].type;
1208 /***********************************************************************
1209 * DRIVE_GetFlags
1211 UINT DRIVE_GetFlags( int drive )
1213 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1214 return DOSDrives[drive].flags;
1217 /***********************************************************************
1218 * DRIVE_GetCodepage
1220 UINT DRIVE_GetCodepage( int drive )
1222 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1223 return DOSDrives[drive].codepage;
1227 /***********************************************************************
1228 * DRIVE_Chdir
1230 int DRIVE_Chdir( int drive, LPCWSTR path )
1232 DOS_FULL_NAME full_name;
1233 WCHAR buffer[MAX_PATHNAME_LEN];
1234 LPSTR unix_cwd;
1235 BY_HANDLE_FILE_INFORMATION info;
1236 TDB *pTask = GlobalLock16(GetCurrentTask());
1238 buffer[0] = 'A' + drive;
1239 buffer[1] = ':';
1240 buffer[2] = 0;
1241 TRACE("(%s,%s)\n", debugstr_w(buffer), debugstr_w(path) );
1242 strncpyW( buffer + 2, path, MAX_PATHNAME_LEN - 2 );
1243 buffer[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1245 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
1246 if (!FILE_Stat( full_name.long_name, &info, NULL )) return 0;
1247 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1249 SetLastError( ERROR_FILE_NOT_FOUND );
1250 return 0;
1252 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
1253 while (*unix_cwd == '/') unix_cwd++;
1255 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1256 'A' + drive, unix_cwd, debugstr_w(full_name.short_name + 3) );
1258 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
1259 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
1260 DOSDrives[drive].dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name.short_name) - 2) * sizeof(WCHAR));
1261 strcpyW(DOSDrives[drive].dos_cwd, full_name.short_name + 3);
1262 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
1264 if (pTask && (pTask->curdrive & 0x80) &&
1265 ((pTask->curdrive & ~0x80) == drive))
1267 WideCharToMultiByte(CP_ACP, 0, full_name.short_name + 2, -1,
1268 pTask->curdir, sizeof(pTask->curdir), NULL, NULL);
1269 DRIVE_LastTask = GetCurrentTask();
1271 return 1;
1275 /***********************************************************************
1276 * DRIVE_Disable
1278 int DRIVE_Disable( int drive )
1280 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1282 SetLastError( ERROR_INVALID_DRIVE );
1283 return 0;
1285 DOSDrives[drive].flags |= DRIVE_DISABLED;
1286 return 1;
1290 /***********************************************************************
1291 * DRIVE_Enable
1293 int DRIVE_Enable( int drive )
1295 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1297 SetLastError( ERROR_INVALID_DRIVE );
1298 return 0;
1300 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1301 return 1;
1305 /***********************************************************************
1306 * DRIVE_SetLogicalMapping
1308 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
1310 /* If new_drive is already valid, do nothing and return 0
1311 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1313 DOSDRIVE *old, *new;
1315 old = DOSDrives + existing_drive;
1316 new = DOSDrives + new_drive;
1318 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
1319 !old->root ||
1320 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
1322 SetLastError( ERROR_INVALID_DRIVE );
1323 return 0;
1326 if ( new->root )
1328 TRACE("Can't map drive %c: to already existing drive %c:\n",
1329 'A' + existing_drive, 'A' + new_drive );
1330 /* it is already mapped there, so return success */
1331 if (!strcmp(old->root,new->root))
1332 return 1;
1333 return 0;
1336 new->root = heap_strdup( old->root );
1337 new->dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(old->dos_cwd) + 1) * sizeof(WCHAR));
1338 strcpyW(new->dos_cwd, old->dos_cwd);
1339 new->unix_cwd = heap_strdup( old->unix_cwd );
1340 new->device = heap_strdup( old->device );
1341 memcpy ( new->label_conf, old->label_conf, 12 );
1342 memcpy ( new->label_read, old->label_read, 12 );
1343 new->serial_conf = old->serial_conf;
1344 new->type = old->type;
1345 new->flags = old->flags;
1346 new->dev = old->dev;
1347 new->ino = old->ino;
1349 TRACE("Drive %c: is now equal to drive %c:\n",
1350 'A' + new_drive, 'A' + existing_drive );
1352 return 1;
1356 /***********************************************************************
1357 * DRIVE_OpenDevice
1359 * Open the drive raw device and return a Unix fd (or -1 on error).
1361 int DRIVE_OpenDevice( int drive, int flags )
1363 if (!DRIVE_IsValid( drive )) return -1;
1364 return open( DOSDrives[drive].device, flags );
1368 /***********************************************************************
1369 * DRIVE_GetFreeSpace
1371 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
1372 PULARGE_INTEGER available )
1374 struct statfs info;
1376 if (!DRIVE_IsValid(drive))
1378 SetLastError( ERROR_PATH_NOT_FOUND );
1379 return 0;
1382 /* FIXME: add autoconf check for this */
1383 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1384 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
1385 #else
1386 if (statfs( DOSDrives[drive].root, &info) < 0)
1387 #endif
1389 FILE_SetDosError();
1390 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
1391 return 0;
1394 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
1395 #ifdef HAVE_STRUCT_STATFS_F_BAVAIL
1396 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
1397 #else
1398 # ifdef HAVE_STRUCT_STATFS_F_BFREE
1399 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
1400 # else
1401 # error "statfs has no bfree/bavail member!"
1402 # endif
1403 #endif
1404 if (DOSDrives[drive].type == DRIVE_CDROM)
1405 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1406 available->QuadPart = 0;
1408 return 1;
1411 /***********************************************************************
1412 * DRIVE_GetCurrentDirectory
1413 * Returns "X:\\path\\etc\\".
1415 * Despite the API description, return required length including the
1416 * terminating null when buffer too small. This is the real behaviour.
1418 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPWSTR buf )
1420 UINT ret;
1421 LPCWSTR dos_cwd = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1422 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1424 ret = strlenW(dos_cwd) + 3; /* length of WHOLE current directory */
1425 if (ret >= buflen) return ret + 1;
1427 strcpyW( buf, driveA_rootW );
1428 buf[0] += DRIVE_GetCurrentDrive();
1429 strcatW( buf, dos_cwd );
1430 return ret;
1434 /***********************************************************************
1435 * DRIVE_BuildEnv
1437 * Build the environment array containing the drives' current directories.
1438 * Resulting pointer must be freed with HeapFree.
1440 char *DRIVE_BuildEnv(void)
1442 int i, length = 0;
1443 LPCWSTR cwd[MAX_DOS_DRIVES];
1444 char *env, *p;
1446 for (i = 0; i < MAX_DOS_DRIVES; i++)
1448 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0])
1449 length += WideCharToMultiByte(DRIVE_GetCodepage(i), 0,
1450 cwd[i], -1, NULL, 0, NULL, NULL) + 7;
1452 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
1453 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
1455 if (cwd[i] && cwd[i][0])
1457 *p++ = '='; *p++ = 'A' + i; *p++ = ':';
1458 *p++ = '='; *p++ = 'A' + i; *p++ = ':'; *p++ = '\\';
1459 WideCharToMultiByte(DRIVE_GetCodepage(i), 0, cwd[i], -1, p, 0x7fffffff, NULL, NULL);
1460 p += strlen(p) + 1;
1463 *p = 0;
1464 return env;
1468 /***********************************************************************
1469 * GetDiskFreeSpace (KERNEL.422)
1471 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
1472 LPDWORD sector_bytes, LPDWORD free_clusters,
1473 LPDWORD total_clusters )
1475 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
1476 free_clusters, total_clusters );
1480 /***********************************************************************
1481 * GetDiskFreeSpaceW (KERNEL32.@)
1483 * Fails if expression resulting from current drive's dir and "root"
1484 * is not a root dir of the target drive.
1486 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1487 * if the corresponding info is unneeded.
1489 * FIXME: needs to support UNC names from Win95 OSR2 on.
1491 * Behaviour under Win95a:
1492 * CurrDir root result
1493 * "E:\\TEST" "E:" FALSE
1494 * "E:\\" "E:" TRUE
1495 * "E:\\" "E" FALSE
1496 * "E:\\" "\\" TRUE
1497 * "E:\\TEST" "\\" TRUE
1498 * "E:\\TEST" ":\\" FALSE
1499 * "E:\\TEST" "E:\\" TRUE
1500 * "E:\\TEST" "" FALSE
1501 * "E:\\" "" FALSE (!)
1502 * "E:\\" 0x0 TRUE
1503 * "E:\\TEST" 0x0 TRUE (!)
1504 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1505 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1507 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1508 LPDWORD sector_bytes, LPDWORD free_clusters,
1509 LPDWORD total_clusters )
1511 int drive, sec_size;
1512 ULARGE_INTEGER size,available;
1513 LPCWSTR path;
1514 DWORD cluster_sec;
1516 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root), cluster_sectors, sector_bytes,
1517 free_clusters, total_clusters);
1519 if (!root || root[0] == '\\' || root[0] == '/')
1520 drive = DRIVE_GetCurrentDrive();
1521 else
1522 if (root[0] && root[1] == ':') /* root contains drive tag */
1524 drive = toupperW(root[0]) - 'A';
1525 path = &root[2];
1526 if (path[0] == '\0')
1528 path = DRIVE_GetDosCwd(drive);
1529 if (!path)
1531 SetLastError(ERROR_PATH_NOT_FOUND);
1532 return FALSE;
1535 else
1536 if (path[0] == '\\')
1537 path++;
1539 if (path[0]) /* oops, we are in a subdir */
1541 SetLastError(ERROR_INVALID_NAME);
1542 return FALSE;
1545 else
1547 if (!root[0])
1548 SetLastError(ERROR_PATH_NOT_FOUND);
1549 else
1550 SetLastError(ERROR_INVALID_NAME);
1551 return FALSE;
1554 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1556 /* Cap the size and available at 2GB as per specs. */
1557 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1559 size.s.HighPart = 0;
1560 size.s.LowPart = 0x7fffffff;
1562 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1564 available.s.HighPart =0;
1565 available.s.LowPart = 0x7fffffff;
1567 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1568 size.s.LowPart /= sec_size;
1569 available.s.LowPart /= sec_size;
1570 /* FIXME: probably have to adjust those variables too for CDFS */
1571 cluster_sec = 1;
1572 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1574 if (cluster_sectors)
1575 *cluster_sectors = cluster_sec;
1576 if (sector_bytes)
1577 *sector_bytes = sec_size;
1578 if (free_clusters)
1579 *free_clusters = available.s.LowPart / cluster_sec;
1580 if (total_clusters)
1581 *total_clusters = size.s.LowPart / cluster_sec;
1582 return TRUE;
1586 /***********************************************************************
1587 * GetDiskFreeSpaceA (KERNEL32.@)
1589 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1590 LPDWORD sector_bytes, LPDWORD free_clusters,
1591 LPDWORD total_clusters )
1593 UNICODE_STRING rootW;
1594 BOOL ret = FALSE;
1596 if (root)
1598 if(!RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1600 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1601 return FALSE;
1604 else
1605 rootW.Buffer = NULL;
1607 ret = GetDiskFreeSpaceW(rootW.Buffer, cluster_sectors, sector_bytes,
1608 free_clusters, total_clusters );
1609 RtlFreeUnicodeString(&rootW);
1611 return ret;
1615 /***********************************************************************
1616 * GetDiskFreeSpaceExW (KERNEL32.@)
1618 * This function is used to acquire the size of the available and
1619 * total space on a logical volume.
1621 * RETURNS
1623 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1624 * detailed error information.
1627 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root,
1628 PULARGE_INTEGER avail,
1629 PULARGE_INTEGER total,
1630 PULARGE_INTEGER totalfree)
1632 int drive;
1633 ULARGE_INTEGER size,available;
1635 if (!root) drive = DRIVE_GetCurrentDrive();
1636 else
1637 { /* C: always works for GetDiskFreeSpaceEx */
1638 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
1640 FIXME("there are valid root names which are not supported yet\n");
1641 /* ..like UNC names, for instance. */
1643 WARN("invalid root '%s'\n", debugstr_w(root));
1644 return FALSE;
1646 drive = toupperW(root[0]) - 'A';
1649 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1651 if (total)
1653 total->s.HighPart = size.s.HighPart;
1654 total->s.LowPart = size.s.LowPart;
1657 if (totalfree)
1659 totalfree->s.HighPart = available.s.HighPart;
1660 totalfree->s.LowPart = available.s.LowPart;
1663 if (avail)
1665 if (FIXME_ON(dosfs))
1667 /* On Windows2000, we need to check the disk quota
1668 allocated for the user owning the calling process. We
1669 don't want to be more obtrusive than necessary with the
1670 FIXME messages, so don't print the FIXME unless Wine is
1671 actually masquerading as Windows2000. */
1673 OSVERSIONINFOA ovi;
1674 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1675 if (GetVersionExA(&ovi))
1677 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1678 FIXME("no per-user quota support yet\n");
1682 /* Quick hack, should eventually be fixed to work 100% with
1683 Windows2000 (see comment above). */
1684 avail->s.HighPart = available.s.HighPart;
1685 avail->s.LowPart = available.s.LowPart;
1688 return TRUE;
1691 /***********************************************************************
1692 * GetDiskFreeSpaceExA (KERNEL32.@)
1694 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
1695 PULARGE_INTEGER total,
1696 PULARGE_INTEGER totalfree)
1698 UNICODE_STRING rootW;
1699 BOOL ret;
1701 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
1702 else rootW.Buffer = NULL;
1704 ret = GetDiskFreeSpaceExW( rootW.Buffer, avail, total, totalfree);
1706 RtlFreeUnicodeString(&rootW);
1707 return ret;
1710 /***********************************************************************
1711 * GetDriveType (KERNEL.136)
1712 * This function returns the type of a drive in Win16.
1713 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1714 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1715 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1716 * do any pseudo-clever changes.
1718 * RETURNS
1719 * drivetype DRIVE_xxx
1721 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1723 UINT type = DRIVE_GetType(drive);
1724 TRACE("(%c:)\n", 'A' + drive );
1725 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1726 return type;
1730 /***********************************************************************
1731 * GetDriveTypeW (KERNEL32.@)
1733 * Returns the type of the disk drive specified. If root is NULL the
1734 * root of the current directory is used.
1736 * RETURNS
1738 * Type of drive (from Win32 SDK):
1740 * DRIVE_UNKNOWN unable to find out anything about the drive
1741 * DRIVE_NO_ROOT_DIR nonexistent root dir
1742 * DRIVE_REMOVABLE the disk can be removed from the machine
1743 * DRIVE_FIXED the disk can not be removed from the machine
1744 * DRIVE_REMOTE network disk
1745 * DRIVE_CDROM CDROM drive
1746 * DRIVE_RAMDISK virtual disk in RAM
1748 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1750 int drive;
1751 TRACE("(%s)\n", debugstr_w(root));
1753 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1754 else
1756 if ((root[1]) && (root[1] != ':'))
1758 WARN("invalid root %s\n", debugstr_w(root));
1759 return DRIVE_NO_ROOT_DIR;
1761 drive = toupperW(root[0]) - 'A';
1763 return DRIVE_GetType(drive);
1767 /***********************************************************************
1768 * GetDriveTypeA (KERNEL32.@)
1770 UINT WINAPI GetDriveTypeA( LPCSTR root )
1772 UNICODE_STRING rootW;
1773 UINT ret = 0;
1775 if (root)
1777 if( !RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1779 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1780 return 0;
1783 else
1784 rootW.Buffer = NULL;
1786 ret = GetDriveTypeW(rootW.Buffer);
1788 RtlFreeUnicodeString(&rootW);
1789 return ret;
1794 /***********************************************************************
1795 * GetCurrentDirectory (KERNEL.411)
1797 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1799 WCHAR cur_dirW[MAX_PATH];
1801 DRIVE_GetCurrentDirectory(MAX_PATH, cur_dirW);
1802 return (UINT16)WideCharToMultiByte(CP_ACP, 0, cur_dirW, -1, buf, buflen, NULL, NULL);
1806 /***********************************************************************
1807 * GetCurrentDirectoryW (KERNEL32.@)
1809 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1811 UINT ret;
1812 WCHAR longname[MAX_PATHNAME_LEN];
1813 WCHAR shortname[MAX_PATHNAME_LEN];
1815 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1816 if ( ret > MAX_PATHNAME_LEN ) {
1817 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1818 return ret;
1820 GetLongPathNameW(shortname, longname, MAX_PATHNAME_LEN);
1821 ret = strlenW( longname ) + 1;
1822 if (ret > buflen) return ret;
1823 strcpyW(buf, longname);
1824 return ret - 1;
1827 /***********************************************************************
1828 * GetCurrentDirectoryA (KERNEL32.@)
1830 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1832 WCHAR bufferW[MAX_PATH];
1833 DWORD ret, retW;
1835 retW = GetCurrentDirectoryW(MAX_PATH, bufferW);
1837 if (!retW)
1838 ret = 0;
1839 else if (retW > MAX_PATH)
1841 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1842 ret = 0;
1844 else
1846 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1847 if (buflen >= ret)
1849 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, buflen, NULL, NULL);
1850 ret--; /* length without 0 */
1853 return ret;
1857 /***********************************************************************
1858 * SetCurrentDirectory (KERNEL.412)
1860 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1862 return SetCurrentDirectoryA( dir );
1866 /***********************************************************************
1867 * SetCurrentDirectoryW (KERNEL32.@)
1869 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dir )
1871 int drive, olddrive = DRIVE_GetCurrentDrive();
1873 if (!dir)
1875 SetLastError(ERROR_INVALID_PARAMETER);
1876 return FALSE;
1878 if (dir[0] && (dir[1]==':'))
1880 drive = toupperW( *dir ) - 'A';
1881 dir += 2;
1883 else
1884 drive = olddrive;
1886 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1887 sets pTask->curdir only if pTask->curdrive is drive */
1888 if (!(DRIVE_SetCurrentDrive( drive )))
1889 return FALSE;
1891 /* FIXME: what about empty strings? Add a \\ ? */
1892 if (!DRIVE_Chdir( drive, dir )) {
1893 DRIVE_SetCurrentDrive(olddrive);
1894 return FALSE;
1896 return TRUE;
1900 /***********************************************************************
1901 * SetCurrentDirectoryA (KERNEL32.@)
1903 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1905 UNICODE_STRING dirW;
1906 BOOL ret = FALSE;
1908 if (!dir)
1910 SetLastError(ERROR_INVALID_PARAMETER);
1911 return FALSE;
1914 if (RtlCreateUnicodeStringFromAsciiz(&dirW, dir))
1916 ret = SetCurrentDirectoryW(dirW.Buffer);
1917 RtlFreeUnicodeString(&dirW);
1919 else
1920 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1921 return ret;
1925 /***********************************************************************
1926 * GetLogicalDriveStringsA (KERNEL32.@)
1928 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1930 int drive, count;
1932 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1933 if (DRIVE_IsValid(drive)) count++;
1934 if ((count * 4) + 1 <= len)
1936 LPSTR p = buffer;
1937 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1938 if (DRIVE_IsValid(drive))
1940 *p++ = 'a' + drive;
1941 *p++ = ':';
1942 *p++ = '\\';
1943 *p++ = '\0';
1945 *p = '\0';
1946 return count * 4;
1948 else
1949 return (count * 4) + 1; /* account for terminating null */
1950 /* The API tells about these different return values */
1954 /***********************************************************************
1955 * GetLogicalDriveStringsW (KERNEL32.@)
1957 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1959 int drive, count;
1961 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1962 if (DRIVE_IsValid(drive)) count++;
1963 if (count * 4 * sizeof(WCHAR) <= len)
1965 LPWSTR p = buffer;
1966 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1967 if (DRIVE_IsValid(drive))
1969 *p++ = (WCHAR)('a' + drive);
1970 *p++ = (WCHAR)':';
1971 *p++ = (WCHAR)'\\';
1972 *p++ = (WCHAR)'\0';
1974 *p = (WCHAR)'\0';
1976 return count * 4 * sizeof(WCHAR);
1980 /***********************************************************************
1981 * GetLogicalDrives (KERNEL32.@)
1983 DWORD WINAPI GetLogicalDrives(void)
1985 DWORD ret = 0;
1986 int drive;
1988 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1990 if ( (DRIVE_IsValid(drive)) ||
1991 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1992 ret |= (1 << drive);
1994 return ret;
1998 /***********************************************************************
1999 * GetVolumeInformationW (KERNEL32.@)
2001 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
2002 DWORD label_len, DWORD *serial,
2003 DWORD *filename_len, DWORD *flags,
2004 LPWSTR fsname, DWORD fsname_len )
2006 int drive;
2007 LPWSTR cp;
2009 /* FIXME, SetLastError()s missing */
2011 if (!root) drive = DRIVE_GetCurrentDrive();
2012 else
2014 if (root[0] && root[1] != ':')
2016 WARN("invalid root %s\n", debugstr_w(root));
2017 return FALSE;
2019 drive = toupperW(root[0]) - 'A';
2021 if (!DRIVE_IsValid( drive )) return FALSE;
2022 if (label && label_len)
2024 strncpyW( label, DRIVE_GetLabel(drive), label_len );
2025 label[label_len - 1] = 0; /* ensure 0 termination */
2026 cp = label + strlenW(label);
2027 while (cp != label && *(cp-1) == ' ') cp--;
2028 *cp = '\0';
2030 if (serial) *serial = DRIVE_GetSerialNumber(drive);
2032 /* Set the filesystem information */
2033 /* Note: we only emulate a FAT fs at present */
2035 if (filename_len) {
2036 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
2037 *filename_len = 12;
2038 else
2039 *filename_len = 255;
2041 if (flags)
2043 *flags=0;
2044 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
2045 *flags|=FS_CASE_SENSITIVE;
2046 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
2047 *flags|=FS_CASE_IS_PRESERVED;
2049 if (fsname && fsname_len)
2051 /* Diablo checks that return code ... */
2052 if (DOSDrives[drive].type == DRIVE_CDROM)
2054 static const WCHAR cdfsW[] = {'C','D','F','S',0};
2055 strncpyW( fsname, cdfsW, fsname_len );
2057 else
2059 static const WCHAR fatW[] = {'F','A','T',0};
2060 strncpyW( fsname, fatW, fsname_len );
2062 fsname[fsname_len - 1] = 0; /* ensure 0 termination */
2064 return TRUE;
2068 /***********************************************************************
2069 * GetVolumeInformationA (KERNEL32.@)
2071 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
2072 DWORD label_len, DWORD *serial,
2073 DWORD *filename_len, DWORD *flags,
2074 LPSTR fsname, DWORD fsname_len )
2076 UNICODE_STRING rootW;
2077 LPWSTR labelW, fsnameW;
2078 BOOL ret;
2080 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2081 else rootW.Buffer = NULL;
2082 labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
2083 fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
2085 if ((ret = GetVolumeInformationW(rootW.Buffer, labelW, label_len, serial,
2086 filename_len, flags, fsnameW, fsname_len)))
2088 if (label) WideCharToMultiByte(CP_ACP, 0, labelW, -1, label, label_len, NULL, NULL);
2089 if (fsname) WideCharToMultiByte(CP_ACP, 0, fsnameW, -1, fsname, fsname_len, NULL, NULL);
2092 RtlFreeUnicodeString(&rootW);
2093 if (labelW) HeapFree( GetProcessHeap(), 0, labelW );
2094 if (fsnameW) HeapFree( GetProcessHeap(), 0, fsnameW );
2095 return ret;
2098 /***********************************************************************
2099 * SetVolumeLabelW (KERNEL32.@)
2101 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR volname )
2103 int drive;
2105 /* FIXME, SetLastErrors missing */
2107 if (!root) drive = DRIVE_GetCurrentDrive();
2108 else
2110 if ((root[1]) && (root[1] != ':'))
2112 WARN("invalid root %s\n", debugstr_w(root));
2113 return FALSE;
2115 drive = toupperW(root[0]) - 'A';
2117 if (!DRIVE_IsValid( drive )) return FALSE;
2119 /* some copy protection stuff check this */
2120 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
2122 strncpyW(DOSDrives[drive].label_conf, volname, 12);
2123 DOSDrives[drive].label_conf[12 - 1] = 0; /* ensure 0 termination */
2124 return TRUE;
2127 /***********************************************************************
2128 * SetVolumeLabelA (KERNEL32.@)
2130 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
2132 UNICODE_STRING rootW, volnameW;
2133 BOOL ret;
2135 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2136 else rootW.Buffer = NULL;
2137 if (volname) RtlCreateUnicodeStringFromAsciiz(&volnameW, volname);
2138 else volnameW.Buffer = NULL;
2140 ret = SetVolumeLabelW( rootW.Buffer, volnameW.Buffer );
2142 RtlFreeUnicodeString(&rootW);
2143 RtlFreeUnicodeString(&volnameW);
2144 return ret;
2147 /***********************************************************************
2148 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
2150 DWORD WINAPI GetVolumeNameForVolumeMountPointW(LPWSTR str, DWORD a, DWORD b)
2152 FIXME("(%s, %lx, %lx): stub\n", debugstr_w(str), a, b);
2153 return 0;