Replaced remaining calls to PROFILE_GetWineIniString/Bool by direct
[wine/wine-kai.git] / files / drive.c
blobbf29d75c6f82fbff59a2b73eba6ddc8138c344f9
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 = TASK_GetCurrent();
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 = TASK_GetCurrent();
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 = TASK_GetCurrent();
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 = TASK_GetCurrent();
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 PROFILE_UsageWineIni();
755 return -1;
758 switch(DOSDrives[drive].type)
760 case DRIVE_REMOVABLE:
761 case DRIVE_FIXED:
762 offs = 0;
763 break;
764 case DRIVE_CDROM:
765 offs = CDROM_Data_FindBestVoldesc(fd);
766 break;
767 default:
768 offs = 0;
769 break;
772 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs))
774 ret = -4;
775 goto the_end;
777 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER)
779 ret = -2;
780 goto the_end;
783 switch(DOSDrives[drive].type)
785 case DRIVE_REMOVABLE:
786 case DRIVE_FIXED:
787 if ((buff[0x26]!=0x29) || /* Check for FAT present */
788 /* FIXME: do really all FAT have their name beginning with
789 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
790 memcmp( buff+0x36,"FAT",3))
792 ERR("The filesystem is not FAT !! (device=%s)\n",
793 DOSDrives[drive].device);
794 ret = -3;
795 goto the_end;
797 break;
798 case DRIVE_CDROM:
799 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
801 ret = -3;
802 goto the_end;
804 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
805 break;
806 default:
807 ret = -3;
808 goto the_end;
811 return close(fd);
812 the_end:
813 close(fd);
814 return ret;
818 /***********************************************************************
819 * DRIVE_WriteSuperblockEntry
821 * NOTE
822 * We are writing as little as possible (ie. not the whole SuperBlock)
823 * not to interfere with kernel. The drive can be mounted !
825 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
827 int fd;
829 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
831 ERR("Cannot open the device %s (for writing)\n",
832 DOSDrives[drive].device);
833 return -1;
835 if (lseek(fd,ofs,SEEK_SET)!=ofs)
837 ERR("lseek failed on device %s !\n",
838 DOSDrives[drive].device);
839 close(fd);
840 return -2;
842 if (write(fd,buff,len)!=len)
844 close(fd);
845 ERR("Cannot write on %s !\n",
846 DOSDrives[drive].device);
847 return -3;
849 return close (fd);
852 /******************************************************************
853 * static HANDLE CDROM_Open
857 static HANDLE CDROM_Open(int drive)
859 WCHAR root[] = {'\\','\\','.','\\','A',':',0};
860 root[4] += drive;
861 return CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
864 /**************************************************************************
865 * CDROM_Data_GetLabel [internal]
867 DWORD CDROM_Data_GetLabel(int drive, WCHAR *label)
869 #define LABEL_LEN 32+1
870 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
871 WORD offs = CDROM_Data_FindBestVoldesc(dev);
872 WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
873 DWORD unicode_id = 0;
875 if (offs)
877 if ((lseek(dev, offs+0x58, SEEK_SET) == offs+0x58)
878 && (read(dev, &unicode_id, 3) == 3))
880 int ver = (unicode_id & 0xff0000) >> 16;
882 if ((lseek(dev, offs+0x28, SEEK_SET) != offs+0x28)
883 || (read(dev, &label_read, LABEL_LEN) != LABEL_LEN))
884 goto failure;
886 close(dev);
887 if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
888 && ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
889 { /* yippee, unicode */
890 int i;
891 WORD ch;
892 for (i=0; i<LABEL_LEN;i++)
893 { /* Motorola -> Intel Unicode conversion :-\ */
894 ch = label_read[i];
895 label_read[i] = (ch << 8) | (ch >> 8);
897 strncpyW(label, label_read, 11);
898 label[11] = 0;
900 else
902 MultiByteToWideChar(DOSDrives[drive].codepage, 0, (LPSTR)label_read, -1, label, 11);
903 label[11] = '\0';
905 return 1;
908 failure:
909 close(dev);
910 ERR("error reading label !\n");
911 return 0;
914 /**************************************************************************
915 * CDROM_GetLabel [internal]
917 static DWORD CDROM_GetLabel(int drive, WCHAR *label)
919 HANDLE h;
920 CDROM_DISK_DATA cdd;
921 DWORD br, ret = 1;
922 BOOL r;
924 h = CDROM_Open(drive);
925 if( !h )
926 return 0;
927 r = DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL,
928 0, &cdd, sizeof(cdd), &br, 0);
929 CloseHandle( h );
930 if( !r )
931 return 0;
933 switch (cdd.DiskData & 0x03)
935 case CDROM_DISK_DATA_TRACK:
936 if (!CDROM_Data_GetLabel(drive, label))
937 ret = 0;
938 break;
939 case CDROM_DISK_AUDIO_TRACK:
941 static const WCHAR audioCD[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
942 strcpyW(label, audioCD);
943 break;
945 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
946 FIXME("Need to get the label of a mixed mode CD!\n");
947 /* This assumes that the first track is a data track! */
948 /* I guess the correct way would be to enumerate all data tracks
949 and check each for the title */
950 if (!CDROM_Data_GetLabel(drive, label))
951 ret = 0;
952 break;
953 case 0:
954 ret = 0;
955 break;
957 TRACE("CD: label is %s\n", debugstr_w(label));
959 return ret;
961 /***********************************************************************
962 * DRIVE_GetLabel
964 LPCWSTR DRIVE_GetLabel( int drive )
966 int read = 0;
967 char buff[DRIVE_SUPER];
968 int offs = -1;
970 if (!DRIVE_IsValid( drive )) return NULL;
971 if (DOSDrives[drive].type == DRIVE_CDROM)
973 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
975 else
976 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
978 if (DRIVE_ReadSuperblock(drive,(char *) buff))
979 ERR("Invalid or unreadable superblock on %s (%c:).\n",
980 DOSDrives[drive].device, (char)(drive+'A'));
981 else {
982 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
983 DOSDrives[drive].type == DRIVE_FIXED)
984 offs = 0x2b;
986 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
987 if (offs != -1)
988 MultiByteToWideChar(DOSDrives[drive].codepage, 0, buff+offs, 11,
989 DOSDrives[drive].label_read, 11);
990 DOSDrives[drive].label_read[11]='\0';
991 read = 1;
995 return (read) ?
996 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
999 #define CDFRAMES_PERSEC 75
1000 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
1001 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
1002 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
1004 /**************************************************************************
1005 * CDROM_Audio_GetSerial [internal]
1007 static DWORD CDROM_Audio_GetSerial(HANDLE h)
1009 unsigned long serial = 0;
1010 int i;
1011 WORD wMagic;
1012 DWORD dwStart, dwEnd, br;
1013 CDROM_TOC toc;
1015 if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
1016 return 0;
1019 * wMagic collects the wFrames from track 1
1020 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
1021 * frames.
1022 * There it is collected for correcting the serial when there are less than
1023 * 3 tracks.
1025 wMagic = toc.TrackData[0].Address[2];
1026 dwStart = FRAME_OF_TOC(toc, toc.FirstTrack);
1028 for (i = 0; i <= toc.LastTrack - toc.FirstTrack; i++) {
1029 serial += (toc.TrackData[i].Address[0] << 16) |
1030 (toc.TrackData[i].Address[1] << 8) | toc.TrackData[i].Address[2];
1032 dwEnd = FRAME_OF_TOC(toc, toc.LastTrack + 1);
1034 if (toc.LastTrack - toc.FirstTrack + 1 < 3)
1035 serial += wMagic + (dwEnd - dwStart);
1037 return serial;
1040 /**************************************************************************
1041 * CDROM_Data_GetSerial [internal]
1043 static DWORD CDROM_Data_GetSerial(int drive)
1045 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
1046 WORD offs;
1047 union {
1048 unsigned long val;
1049 unsigned char p[4];
1050 } serial;
1051 BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
1054 if (dev == -1) return 0;
1055 offs = CDROM_Data_FindBestVoldesc(dev);
1057 serial.val = 0;
1058 if (offs)
1060 BYTE buf[2048];
1061 OSVERSIONINFOA ovi;
1062 int i;
1064 lseek(dev, offs, SEEK_SET);
1065 read(dev, buf, 2048);
1067 * OK, another braindead one... argh. Just believe it.
1068 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
1069 * It's true and nobody will ever be able to change it.
1071 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1072 GetVersionExA(&ovi);
1073 if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (ovi.dwMajorVersion >= 4))
1075 b0 = 3; b1 = 2; b2 = 1; b3 = 0;
1077 for (i = 0; i < 2048; i += 4)
1079 /* DON'T optimize this into DWORD !! (breaks overflow) */
1080 serial.p[b0] += buf[i+b0];
1081 serial.p[b1] += buf[i+b1];
1082 serial.p[b2] += buf[i+b2];
1083 serial.p[b3] += buf[i+b3];
1086 close(dev);
1087 return serial.val;
1090 /**************************************************************************
1091 * CDROM_GetSerial [internal]
1093 static DWORD CDROM_GetSerial(int drive)
1095 DWORD serial = 0;
1096 HANDLE h;
1097 CDROM_DISK_DATA cdd;
1098 DWORD br;
1099 BOOL r;
1101 TRACE("%d\n", drive);
1103 h = CDROM_Open(drive);
1104 if( !h )
1105 return 0;
1106 r = DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL,
1107 0, &cdd, sizeof(cdd), &br, 0);
1108 if (!r)
1110 CloseHandle(h);
1111 return 0;
1114 switch (cdd.DiskData & 0x03)
1116 case CDROM_DISK_DATA_TRACK:
1117 /* hopefully a data CD */
1118 serial = CDROM_Data_GetSerial(drive);
1119 break;
1120 case CDROM_DISK_AUDIO_TRACK:
1121 /* fall thru */
1122 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
1123 serial = CDROM_Audio_GetSerial(h);
1124 break;
1125 case 0:
1126 break;
1129 if (serial)
1130 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
1132 CloseHandle(h);
1134 return serial;
1137 /***********************************************************************
1138 * DRIVE_GetSerialNumber
1140 DWORD DRIVE_GetSerialNumber( int drive )
1142 DWORD serial = 0;
1143 char buff[DRIVE_SUPER];
1145 TRACE("drive %d, type = %d\n", drive, DOSDrives[drive].type);
1147 if (!DRIVE_IsValid( drive )) return 0;
1149 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1151 switch(DOSDrives[drive].type)
1153 case DRIVE_REMOVABLE:
1154 case DRIVE_FIXED:
1155 if (DRIVE_ReadSuperblock(drive,(char *) buff))
1156 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
1157 " Maybe not FAT?\n" ,
1158 DOSDrives[drive].device, 'A'+drive);
1159 else
1160 serial = *((DWORD*)(buff+0x27));
1161 break;
1162 case DRIVE_CDROM:
1163 serial = CDROM_GetSerial(drive);
1164 break;
1165 default:
1166 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
1170 return (serial) ? serial : DOSDrives[drive].serial_conf;
1174 /***********************************************************************
1175 * DRIVE_SetSerialNumber
1177 int DRIVE_SetSerialNumber( int drive, DWORD serial )
1179 char buff[DRIVE_SUPER];
1181 if (!DRIVE_IsValid( drive )) return 0;
1183 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1185 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
1186 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
1187 /* check, if the drive has a FAT filesystem */
1188 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
1189 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
1190 return 1;
1193 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
1194 DOSDrives[drive].serial_conf = serial;
1195 return 1;
1199 /***********************************************************************
1200 * DRIVE_GetType
1202 static UINT DRIVE_GetType( int drive )
1204 if (!DRIVE_IsValid( drive )) return DRIVE_NO_ROOT_DIR;
1205 return DOSDrives[drive].type;
1209 /***********************************************************************
1210 * DRIVE_GetFlags
1212 UINT DRIVE_GetFlags( int drive )
1214 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1215 return DOSDrives[drive].flags;
1218 /***********************************************************************
1219 * DRIVE_GetCodepage
1221 UINT DRIVE_GetCodepage( int drive )
1223 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1224 return DOSDrives[drive].codepage;
1228 /***********************************************************************
1229 * DRIVE_Chdir
1231 int DRIVE_Chdir( int drive, LPCWSTR path )
1233 DOS_FULL_NAME full_name;
1234 WCHAR buffer[MAX_PATHNAME_LEN];
1235 LPSTR unix_cwd;
1236 BY_HANDLE_FILE_INFORMATION info;
1237 TDB *pTask = TASK_GetCurrent();
1239 buffer[0] = 'A' + drive;
1240 buffer[1] = ':';
1241 buffer[2] = 0;
1242 TRACE("(%s,%s)\n", debugstr_w(buffer), debugstr_w(path) );
1243 strncpyW( buffer + 2, path, MAX_PATHNAME_LEN - 2 );
1244 buffer[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1246 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
1247 if (!FILE_Stat( full_name.long_name, &info, NULL )) return 0;
1248 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1250 SetLastError( ERROR_FILE_NOT_FOUND );
1251 return 0;
1253 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
1254 while (*unix_cwd == '/') unix_cwd++;
1256 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1257 'A' + drive, unix_cwd, debugstr_w(full_name.short_name + 3) );
1259 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
1260 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
1261 DOSDrives[drive].dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name.short_name) - 2) * sizeof(WCHAR));
1262 strcpyW(DOSDrives[drive].dos_cwd, full_name.short_name + 3);
1263 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
1265 if (pTask && (pTask->curdrive & 0x80) &&
1266 ((pTask->curdrive & ~0x80) == drive))
1268 WideCharToMultiByte(CP_ACP, 0, full_name.short_name + 2, -1,
1269 pTask->curdir, sizeof(pTask->curdir), NULL, NULL);
1270 DRIVE_LastTask = GetCurrentTask();
1272 return 1;
1276 /***********************************************************************
1277 * DRIVE_Disable
1279 int DRIVE_Disable( int drive )
1281 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1283 SetLastError( ERROR_INVALID_DRIVE );
1284 return 0;
1286 DOSDrives[drive].flags |= DRIVE_DISABLED;
1287 return 1;
1291 /***********************************************************************
1292 * DRIVE_Enable
1294 int DRIVE_Enable( int drive )
1296 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1298 SetLastError( ERROR_INVALID_DRIVE );
1299 return 0;
1301 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1302 return 1;
1306 /***********************************************************************
1307 * DRIVE_SetLogicalMapping
1309 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
1311 /* If new_drive is already valid, do nothing and return 0
1312 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1314 DOSDRIVE *old, *new;
1316 old = DOSDrives + existing_drive;
1317 new = DOSDrives + new_drive;
1319 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
1320 !old->root ||
1321 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
1323 SetLastError( ERROR_INVALID_DRIVE );
1324 return 0;
1327 if ( new->root )
1329 TRACE("Can't map drive %c: to already existing drive %c:\n",
1330 'A' + existing_drive, 'A' + new_drive );
1331 /* it is already mapped there, so return success */
1332 if (!strcmp(old->root,new->root))
1333 return 1;
1334 return 0;
1337 new->root = heap_strdup( old->root );
1338 new->dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(old->dos_cwd) + 1) * sizeof(WCHAR));
1339 strcpyW(new->dos_cwd, old->dos_cwd);
1340 new->unix_cwd = heap_strdup( old->unix_cwd );
1341 new->device = heap_strdup( old->device );
1342 memcpy ( new->label_conf, old->label_conf, 12 );
1343 memcpy ( new->label_read, old->label_read, 12 );
1344 new->serial_conf = old->serial_conf;
1345 new->type = old->type;
1346 new->flags = old->flags;
1347 new->dev = old->dev;
1348 new->ino = old->ino;
1350 TRACE("Drive %c: is now equal to drive %c:\n",
1351 'A' + new_drive, 'A' + existing_drive );
1353 return 1;
1357 /***********************************************************************
1358 * DRIVE_OpenDevice
1360 * Open the drive raw device and return a Unix fd (or -1 on error).
1362 int DRIVE_OpenDevice( int drive, int flags )
1364 if (!DRIVE_IsValid( drive )) return -1;
1365 return open( DOSDrives[drive].device, flags );
1369 /***********************************************************************
1370 * DRIVE_GetFreeSpace
1372 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
1373 PULARGE_INTEGER available )
1375 struct statfs info;
1377 if (!DRIVE_IsValid(drive))
1379 SetLastError( ERROR_PATH_NOT_FOUND );
1380 return 0;
1383 /* FIXME: add autoconf check for this */
1384 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1385 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
1386 #else
1387 if (statfs( DOSDrives[drive].root, &info) < 0)
1388 #endif
1390 FILE_SetDosError();
1391 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
1392 return 0;
1395 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
1396 #ifdef HAVE_STRUCT_STATFS_F_BAVAIL
1397 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
1398 #else
1399 # ifdef HAVE_STRUCT_STATFS_F_BFREE
1400 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
1401 # else
1402 # error "statfs has no bfree/bavail member!"
1403 # endif
1404 #endif
1405 if (DOSDrives[drive].type == DRIVE_CDROM)
1406 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1407 available->QuadPart = 0;
1409 return 1;
1412 /***********************************************************************
1413 * DRIVE_GetCurrentDirectory
1414 * Returns "X:\\path\\etc\\".
1416 * Despite the API description, return required length including the
1417 * terminating null when buffer too small. This is the real behaviour.
1419 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPWSTR buf )
1421 UINT ret;
1422 LPCWSTR dos_cwd = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1423 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1425 ret = strlenW(dos_cwd) + 3; /* length of WHOLE current directory */
1426 if (ret >= buflen) return ret + 1;
1428 strcpyW( buf, driveA_rootW );
1429 buf[0] += DRIVE_GetCurrentDrive();
1430 strcatW( buf, dos_cwd );
1431 return ret;
1435 /***********************************************************************
1436 * DRIVE_BuildEnv
1438 * Build the environment array containing the drives' current directories.
1439 * Resulting pointer must be freed with HeapFree.
1441 char *DRIVE_BuildEnv(void)
1443 int i, length = 0;
1444 LPCWSTR cwd[MAX_DOS_DRIVES];
1445 char *env, *p;
1447 for (i = 0; i < MAX_DOS_DRIVES; i++)
1449 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0])
1450 length += WideCharToMultiByte(DRIVE_GetCodepage(i), 0,
1451 cwd[i], -1, NULL, 0, NULL, NULL) + 7;
1453 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
1454 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
1456 if (cwd[i] && cwd[i][0])
1458 *p++ = '='; *p++ = 'A' + i; *p++ = ':';
1459 *p++ = '='; *p++ = 'A' + i; *p++ = ':'; *p++ = '\\';
1460 WideCharToMultiByte(DRIVE_GetCodepage(i), 0, cwd[i], -1, p, 0x7fffffff, NULL, NULL);
1461 p += strlen(p) + 1;
1464 *p = 0;
1465 return env;
1469 /***********************************************************************
1470 * GetDiskFreeSpace (KERNEL.422)
1472 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
1473 LPDWORD sector_bytes, LPDWORD free_clusters,
1474 LPDWORD total_clusters )
1476 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
1477 free_clusters, total_clusters );
1481 /***********************************************************************
1482 * GetDiskFreeSpaceW (KERNEL32.@)
1484 * Fails if expression resulting from current drive's dir and "root"
1485 * is not a root dir of the target drive.
1487 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1488 * if the corresponding info is unneeded.
1490 * FIXME: needs to support UNC names from Win95 OSR2 on.
1492 * Behaviour under Win95a:
1493 * CurrDir root result
1494 * "E:\\TEST" "E:" FALSE
1495 * "E:\\" "E:" TRUE
1496 * "E:\\" "E" FALSE
1497 * "E:\\" "\\" TRUE
1498 * "E:\\TEST" "\\" TRUE
1499 * "E:\\TEST" ":\\" FALSE
1500 * "E:\\TEST" "E:\\" TRUE
1501 * "E:\\TEST" "" FALSE
1502 * "E:\\" "" FALSE (!)
1503 * "E:\\" 0x0 TRUE
1504 * "E:\\TEST" 0x0 TRUE (!)
1505 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1506 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1508 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1509 LPDWORD sector_bytes, LPDWORD free_clusters,
1510 LPDWORD total_clusters )
1512 int drive, sec_size;
1513 ULARGE_INTEGER size,available;
1514 LPCWSTR path;
1515 DWORD cluster_sec;
1517 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root), cluster_sectors, sector_bytes,
1518 free_clusters, total_clusters);
1520 if (!root || root[0] == '\\' || root[0] == '/')
1521 drive = DRIVE_GetCurrentDrive();
1522 else
1523 if (root[0] && root[1] == ':') /* root contains drive tag */
1525 drive = toupperW(root[0]) - 'A';
1526 path = &root[2];
1527 if (path[0] == '\0')
1529 path = DRIVE_GetDosCwd(drive);
1530 if (!path)
1532 SetLastError(ERROR_PATH_NOT_FOUND);
1533 return FALSE;
1536 else
1537 if (path[0] == '\\')
1538 path++;
1540 if (path[0]) /* oops, we are in a subdir */
1542 SetLastError(ERROR_INVALID_NAME);
1543 return FALSE;
1546 else
1548 if (!root[0])
1549 SetLastError(ERROR_PATH_NOT_FOUND);
1550 else
1551 SetLastError(ERROR_INVALID_NAME);
1552 return FALSE;
1555 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1557 /* Cap the size and available at 2GB as per specs. */
1558 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1560 size.s.HighPart = 0;
1561 size.s.LowPart = 0x7fffffff;
1563 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1565 available.s.HighPart =0;
1566 available.s.LowPart = 0x7fffffff;
1568 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1569 size.s.LowPart /= sec_size;
1570 available.s.LowPart /= sec_size;
1571 /* FIXME: probably have to adjust those variables too for CDFS */
1572 cluster_sec = 1;
1573 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1575 if (cluster_sectors)
1576 *cluster_sectors = cluster_sec;
1577 if (sector_bytes)
1578 *sector_bytes = sec_size;
1579 if (free_clusters)
1580 *free_clusters = available.s.LowPart / cluster_sec;
1581 if (total_clusters)
1582 *total_clusters = size.s.LowPart / cluster_sec;
1583 return TRUE;
1587 /***********************************************************************
1588 * GetDiskFreeSpaceA (KERNEL32.@)
1590 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1591 LPDWORD sector_bytes, LPDWORD free_clusters,
1592 LPDWORD total_clusters )
1594 UNICODE_STRING rootW;
1595 BOOL ret = FALSE;
1597 if (root)
1599 if(!RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1601 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1602 return FALSE;
1605 else
1606 rootW.Buffer = NULL;
1608 ret = GetDiskFreeSpaceW(rootW.Buffer, cluster_sectors, sector_bytes,
1609 free_clusters, total_clusters );
1610 RtlFreeUnicodeString(&rootW);
1612 return ret;
1616 /***********************************************************************
1617 * GetDiskFreeSpaceExW (KERNEL32.@)
1619 * This function is used to acquire the size of the available and
1620 * total space on a logical volume.
1622 * RETURNS
1624 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1625 * detailed error information.
1628 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root,
1629 PULARGE_INTEGER avail,
1630 PULARGE_INTEGER total,
1631 PULARGE_INTEGER totalfree)
1633 int drive;
1634 ULARGE_INTEGER size,available;
1636 if (!root) drive = DRIVE_GetCurrentDrive();
1637 else
1638 { /* C: always works for GetDiskFreeSpaceEx */
1639 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
1641 FIXME("there are valid root names which are not supported yet\n");
1642 /* ..like UNC names, for instance. */
1644 WARN("invalid root '%s'\n", debugstr_w(root));
1645 return FALSE;
1647 drive = toupperW(root[0]) - 'A';
1650 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1652 if (total)
1654 total->s.HighPart = size.s.HighPart;
1655 total->s.LowPart = size.s.LowPart;
1658 if (totalfree)
1660 totalfree->s.HighPart = available.s.HighPart;
1661 totalfree->s.LowPart = available.s.LowPart;
1664 if (avail)
1666 if (FIXME_ON(dosfs))
1668 /* On Windows2000, we need to check the disk quota
1669 allocated for the user owning the calling process. We
1670 don't want to be more obtrusive than necessary with the
1671 FIXME messages, so don't print the FIXME unless Wine is
1672 actually masquerading as Windows2000. */
1674 OSVERSIONINFOA ovi;
1675 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1676 if (GetVersionExA(&ovi))
1678 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1679 FIXME("no per-user quota support yet\n");
1683 /* Quick hack, should eventually be fixed to work 100% with
1684 Windows2000 (see comment above). */
1685 avail->s.HighPart = available.s.HighPart;
1686 avail->s.LowPart = available.s.LowPart;
1689 return TRUE;
1692 /***********************************************************************
1693 * GetDiskFreeSpaceExA (KERNEL32.@)
1695 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
1696 PULARGE_INTEGER total,
1697 PULARGE_INTEGER totalfree)
1699 UNICODE_STRING rootW;
1700 BOOL ret;
1702 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
1703 else rootW.Buffer = NULL;
1705 ret = GetDiskFreeSpaceExW( rootW.Buffer, avail, total, totalfree);
1707 RtlFreeUnicodeString(&rootW);
1708 return ret;
1711 /***********************************************************************
1712 * GetDriveType (KERNEL.136)
1713 * This function returns the type of a drive in Win16.
1714 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1715 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1716 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1717 * do any pseudo-clever changes.
1719 * RETURNS
1720 * drivetype DRIVE_xxx
1722 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1724 UINT type = DRIVE_GetType(drive);
1725 TRACE("(%c:)\n", 'A' + drive );
1726 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1727 return type;
1731 /***********************************************************************
1732 * GetDriveTypeW (KERNEL32.@)
1734 * Returns the type of the disk drive specified. If root is NULL the
1735 * root of the current directory is used.
1737 * RETURNS
1739 * Type of drive (from Win32 SDK):
1741 * DRIVE_UNKNOWN unable to find out anything about the drive
1742 * DRIVE_NO_ROOT_DIR nonexistent root dir
1743 * DRIVE_REMOVABLE the disk can be removed from the machine
1744 * DRIVE_FIXED the disk can not be removed from the machine
1745 * DRIVE_REMOTE network disk
1746 * DRIVE_CDROM CDROM drive
1747 * DRIVE_RAMDISK virtual disk in RAM
1749 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1751 int drive;
1752 TRACE("(%s)\n", debugstr_w(root));
1754 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1755 else
1757 if ((root[1]) && (root[1] != ':'))
1759 WARN("invalid root %s\n", debugstr_w(root));
1760 return DRIVE_NO_ROOT_DIR;
1762 drive = toupperW(root[0]) - 'A';
1764 return DRIVE_GetType(drive);
1768 /***********************************************************************
1769 * GetDriveTypeA (KERNEL32.@)
1771 UINT WINAPI GetDriveTypeA( LPCSTR root )
1773 UNICODE_STRING rootW;
1774 UINT ret = 0;
1776 if (root)
1778 if( !RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1780 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1781 return 0;
1784 else
1785 rootW.Buffer = NULL;
1787 ret = GetDriveTypeW(rootW.Buffer);
1789 RtlFreeUnicodeString(&rootW);
1790 return ret;
1795 /***********************************************************************
1796 * GetCurrentDirectory (KERNEL.411)
1798 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1800 WCHAR cur_dirW[MAX_PATH];
1802 DRIVE_GetCurrentDirectory(MAX_PATH, cur_dirW);
1803 return (UINT16)WideCharToMultiByte(CP_ACP, 0, cur_dirW, -1, buf, buflen, NULL, NULL);
1807 /***********************************************************************
1808 * GetCurrentDirectoryW (KERNEL32.@)
1810 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1812 UINT ret;
1813 WCHAR longname[MAX_PATHNAME_LEN];
1814 WCHAR shortname[MAX_PATHNAME_LEN];
1816 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1817 if ( ret > MAX_PATHNAME_LEN ) {
1818 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1819 return ret;
1821 GetLongPathNameW(shortname, longname, MAX_PATHNAME_LEN);
1822 ret = strlenW( longname ) + 1;
1823 if (ret > buflen) return ret;
1824 strcpyW(buf, longname);
1825 return ret - 1;
1828 /***********************************************************************
1829 * GetCurrentDirectoryA (KERNEL32.@)
1831 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1833 WCHAR bufferW[MAX_PATH];
1834 DWORD ret, retW;
1836 retW = GetCurrentDirectoryW(MAX_PATH, bufferW);
1838 if (!retW)
1839 ret = 0;
1840 else if (retW > MAX_PATH)
1842 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1843 ret = 0;
1845 else
1847 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1848 if (buflen >= ret)
1850 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, buflen, NULL, NULL);
1851 ret--; /* length without 0 */
1854 return ret;
1858 /***********************************************************************
1859 * SetCurrentDirectory (KERNEL.412)
1861 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1863 return SetCurrentDirectoryA( dir );
1867 /***********************************************************************
1868 * SetCurrentDirectoryW (KERNEL32.@)
1870 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dir )
1872 int drive, olddrive = DRIVE_GetCurrentDrive();
1874 if (!dir)
1876 SetLastError(ERROR_INVALID_PARAMETER);
1877 return FALSE;
1879 if (dir[0] && (dir[1]==':'))
1881 drive = toupperW( *dir ) - 'A';
1882 dir += 2;
1884 else
1885 drive = olddrive;
1887 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1888 sets pTask->curdir only if pTask->curdrive is drive */
1889 if (!(DRIVE_SetCurrentDrive( drive )))
1890 return FALSE;
1892 /* FIXME: what about empty strings? Add a \\ ? */
1893 if (!DRIVE_Chdir( drive, dir )) {
1894 DRIVE_SetCurrentDrive(olddrive);
1895 return FALSE;
1897 return TRUE;
1901 /***********************************************************************
1902 * SetCurrentDirectoryA (KERNEL32.@)
1904 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1906 UNICODE_STRING dirW;
1907 BOOL ret = FALSE;
1909 if (!dir)
1911 SetLastError(ERROR_INVALID_PARAMETER);
1912 return FALSE;
1915 if (RtlCreateUnicodeStringFromAsciiz(&dirW, dir))
1917 ret = SetCurrentDirectoryW(dirW.Buffer);
1918 RtlFreeUnicodeString(&dirW);
1920 else
1921 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1922 return ret;
1926 /***********************************************************************
1927 * GetLogicalDriveStringsA (KERNEL32.@)
1929 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1931 int drive, count;
1933 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1934 if (DRIVE_IsValid(drive)) count++;
1935 if ((count * 4) + 1 <= len)
1937 LPSTR p = buffer;
1938 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1939 if (DRIVE_IsValid(drive))
1941 *p++ = 'a' + drive;
1942 *p++ = ':';
1943 *p++ = '\\';
1944 *p++ = '\0';
1946 *p = '\0';
1947 return count * 4;
1949 else
1950 return (count * 4) + 1; /* account for terminating null */
1951 /* The API tells about these different return values */
1955 /***********************************************************************
1956 * GetLogicalDriveStringsW (KERNEL32.@)
1958 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1960 int drive, count;
1962 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1963 if (DRIVE_IsValid(drive)) count++;
1964 if (count * 4 * sizeof(WCHAR) <= len)
1966 LPWSTR p = buffer;
1967 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1968 if (DRIVE_IsValid(drive))
1970 *p++ = (WCHAR)('a' + drive);
1971 *p++ = (WCHAR)':';
1972 *p++ = (WCHAR)'\\';
1973 *p++ = (WCHAR)'\0';
1975 *p = (WCHAR)'\0';
1977 return count * 4 * sizeof(WCHAR);
1981 /***********************************************************************
1982 * GetLogicalDrives (KERNEL32.@)
1984 DWORD WINAPI GetLogicalDrives(void)
1986 DWORD ret = 0;
1987 int drive;
1989 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1991 if ( (DRIVE_IsValid(drive)) ||
1992 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1993 ret |= (1 << drive);
1995 return ret;
1999 /***********************************************************************
2000 * GetVolumeInformationW (KERNEL32.@)
2002 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
2003 DWORD label_len, DWORD *serial,
2004 DWORD *filename_len, DWORD *flags,
2005 LPWSTR fsname, DWORD fsname_len )
2007 int drive;
2008 LPWSTR cp;
2010 /* FIXME, SetLastError()s missing */
2012 if (!root) drive = DRIVE_GetCurrentDrive();
2013 else
2015 if (root[0] && root[1] != ':')
2017 WARN("invalid root %s\n", debugstr_w(root));
2018 return FALSE;
2020 drive = toupperW(root[0]) - 'A';
2022 if (!DRIVE_IsValid( drive )) return FALSE;
2023 if (label && label_len)
2025 strncpyW( label, DRIVE_GetLabel(drive), label_len );
2026 label[label_len - 1] = 0; /* ensure 0 termination */
2027 cp = label + strlenW(label);
2028 while (cp != label && *(cp-1) == ' ') cp--;
2029 *cp = '\0';
2031 if (serial) *serial = DRIVE_GetSerialNumber(drive);
2033 /* Set the filesystem information */
2034 /* Note: we only emulate a FAT fs at present */
2036 if (filename_len) {
2037 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
2038 *filename_len = 12;
2039 else
2040 *filename_len = 255;
2042 if (flags)
2044 *flags=0;
2045 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
2046 *flags|=FS_CASE_SENSITIVE;
2047 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
2048 *flags|=FS_CASE_IS_PRESERVED;
2050 if (fsname && fsname_len)
2052 /* Diablo checks that return code ... */
2053 if (DOSDrives[drive].type == DRIVE_CDROM)
2055 static const WCHAR cdfsW[] = {'C','D','F','S',0};
2056 strncpyW( fsname, cdfsW, fsname_len );
2058 else
2060 static const WCHAR fatW[] = {'F','A','T',0};
2061 strncpyW( fsname, fatW, fsname_len );
2063 fsname[fsname_len - 1] = 0; /* ensure 0 termination */
2065 return TRUE;
2069 /***********************************************************************
2070 * GetVolumeInformationA (KERNEL32.@)
2072 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
2073 DWORD label_len, DWORD *serial,
2074 DWORD *filename_len, DWORD *flags,
2075 LPSTR fsname, DWORD fsname_len )
2077 UNICODE_STRING rootW;
2078 LPWSTR labelW, fsnameW;
2079 BOOL ret;
2081 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2082 else rootW.Buffer = NULL;
2083 labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
2084 fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
2086 if ((ret = GetVolumeInformationW(rootW.Buffer, labelW, label_len, serial,
2087 filename_len, flags, fsnameW, fsname_len)))
2089 if (label) WideCharToMultiByte(CP_ACP, 0, labelW, -1, label, label_len, NULL, NULL);
2090 if (fsname) WideCharToMultiByte(CP_ACP, 0, fsnameW, -1, fsname, fsname_len, NULL, NULL);
2093 RtlFreeUnicodeString(&rootW);
2094 if (labelW) HeapFree( GetProcessHeap(), 0, labelW );
2095 if (fsnameW) HeapFree( GetProcessHeap(), 0, fsnameW );
2096 return ret;
2099 /***********************************************************************
2100 * SetVolumeLabelW (KERNEL32.@)
2102 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR volname )
2104 int drive;
2106 /* FIXME, SetLastErrors missing */
2108 if (!root) drive = DRIVE_GetCurrentDrive();
2109 else
2111 if ((root[1]) && (root[1] != ':'))
2113 WARN("invalid root %s\n", debugstr_w(root));
2114 return FALSE;
2116 drive = toupperW(root[0]) - 'A';
2118 if (!DRIVE_IsValid( drive )) return FALSE;
2120 /* some copy protection stuff check this */
2121 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
2123 strncpyW(DOSDrives[drive].label_conf, volname, 12);
2124 DOSDrives[drive].label_conf[12 - 1] = 0; /* ensure 0 termination */
2125 return TRUE;
2128 /***********************************************************************
2129 * SetVolumeLabelA (KERNEL32.@)
2131 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
2133 UNICODE_STRING rootW, volnameW;
2134 BOOL ret;
2136 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2137 else rootW.Buffer = NULL;
2138 if (volname) RtlCreateUnicodeStringFromAsciiz(&volnameW, volname);
2139 else volnameW.Buffer = NULL;
2141 ret = SetVolumeLabelW( rootW.Buffer, volnameW.Buffer );
2143 RtlFreeUnicodeString(&rootW);
2144 RtlFreeUnicodeString(&volnameW);
2145 return ret;
2148 /***********************************************************************
2149 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
2151 DWORD WINAPI GetVolumeNameForVolumeMountPointW(LPWSTR str, DWORD a, DWORD b)
2153 FIXME("(%s, %lx, %lx): stub\n", debugstr_w(str), a, b);
2154 return 0;