Add support for Darwin's ptrace.
[wine/dcerpc.git] / files / drive.c
blobc8a9f88494743118fc93380564908a624b4a61f4
1 /*
2 * DOS drives handling functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
7 * Label & serial number read support.
8 * (c) 1999 Petr Tomasek <tomasek@etf.cuni.cz>
9 * (c) 2000 Andreas Mohr (changes)
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "config.h"
27 #include "wine/port.h"
29 #include <assert.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #ifdef HAVE_UNISTD_H
39 # include <unistd.h>
40 #endif
42 #ifdef HAVE_SYS_PARAM_H
43 # include <sys/param.h>
44 #endif
45 #ifdef STATFS_DEFINED_BY_SYS_VFS
46 # include <sys/vfs.h>
47 #else
48 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
49 # include <sys/mount.h>
50 # else
51 # ifdef STATFS_DEFINED_BY_SYS_STATFS
52 # include <sys/statfs.h>
53 # endif
54 # endif
55 #endif
57 #define NONAMELESSUNION
58 #define NONAMELESSSTRUCT
59 #include "winbase.h"
60 #include "winternl.h"
61 #include "wine/winbase16.h" /* for GetCurrentTask */
62 #include "winerror.h"
63 #include "winioctl.h"
64 #include "ntddstor.h"
65 #include "ntddcdrm.h"
66 #include "drive.h"
67 #include "file.h"
68 #include "msdos.h"
69 #include "task.h"
70 #include "wine/unicode.h"
71 #include "wine/library.h"
72 #include "wine/server.h"
73 #include "wine/debug.h"
75 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
76 WINE_DECLARE_DEBUG_CHANNEL(file);
78 typedef struct
80 char *root; /* root dir in Unix format without trailing / */
81 LPWSTR dos_cwd; /* cwd in DOS format without leading or trailing \ */
82 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
83 char *device; /* raw device path */
84 WCHAR label_conf[12]; /* drive label as cfg'd in wine config */
85 WCHAR label_read[12]; /* drive label as read from device */
86 DWORD serial_conf; /* drive serial number as cfg'd in wine config */
87 UINT type; /* drive type */
88 UINT flags; /* drive flags */
89 UINT codepage; /* drive code page */
90 dev_t dev; /* unix device number */
91 ino_t ino; /* unix inode number */
92 } DOSDRIVE;
95 static const WCHAR DRIVE_Types[][8] =
97 { 0 }, /* DRIVE_UNKNOWN */
98 { 0 }, /* DRIVE_NO_ROOT_DIR */
99 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
100 {'h','d',0}, /* DRIVE_FIXED */
101 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
102 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
103 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
107 /* Known filesystem types */
109 typedef struct
111 const WCHAR name[6];
112 UINT flags;
113 } FS_DESCR;
115 static const FS_DESCR DRIVE_Filesystems[] =
117 { {'u','n','i','x',0}, DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
118 { {'m','s','d','o','s',0}, DRIVE_SHORT_NAMES },
119 { {'d','o','s',0}, DRIVE_SHORT_NAMES },
120 { {'f','a','t',0}, DRIVE_SHORT_NAMES },
121 { {'v','f','a','t',0}, DRIVE_CASE_PRESERVING },
122 { {'w','i','n','9','5',0}, DRIVE_CASE_PRESERVING },
123 { { 0 }, 0 }
127 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
128 static int DRIVE_CurDrive = -1;
130 static HTASK16 DRIVE_LastTask = 0;
132 /* strdup on the process heap */
133 inline static char *heap_strdup( const char *str )
135 INT len = strlen(str) + 1;
136 LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
137 if (p) memcpy( p, str, len );
138 return p;
141 extern void CDROM_InitRegistry(int dev);
143 /***********************************************************************
144 * DRIVE_GetDriveType
146 static UINT DRIVE_GetDriveType( LPCWSTR name )
148 WCHAR buffer[20];
149 int i;
150 static const WCHAR TypeW[] = {'T','y','p','e',0};
151 static const WCHAR hdW[] = {'h','d',0};
153 PROFILE_GetWineIniString( name, TypeW, hdW, buffer, 20 );
154 if(!buffer[0])
155 strcpyW(buffer,hdW);
156 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
158 if (!strcmpiW( buffer, DRIVE_Types[i] )) return i;
160 MESSAGE("%s: unknown drive type %s, defaulting to 'hd'.\n",
161 debugstr_w(name), debugstr_w(buffer) );
162 return DRIVE_FIXED;
166 /***********************************************************************
167 * DRIVE_GetFSFlags
169 static UINT DRIVE_GetFSFlags( LPCWSTR name, LPCWSTR value )
171 const FS_DESCR *descr;
173 for (descr = DRIVE_Filesystems; *descr->name; descr++)
174 if (!strcmpiW( value, descr->name )) return descr->flags;
175 MESSAGE("%s: unknown filesystem type %s, defaulting to 'win95'.\n",
176 debugstr_w(name), debugstr_w(value) );
177 return DRIVE_CASE_PRESERVING;
181 /***********************************************************************
182 * DRIVE_Init
184 int DRIVE_Init(void)
186 int i, len, count = 0;
187 WCHAR name[] = {'D','r','i','v','e',' ','A',0};
188 WCHAR drive_env[] = {'=','A',':',0};
189 WCHAR path[MAX_PATHNAME_LEN];
190 WCHAR buffer[80];
191 struct stat drive_stat_buffer;
192 WCHAR *p;
193 DOSDRIVE *drive;
194 static const WCHAR PathW[] = {'P','a','t','h',0};
195 static const WCHAR empty_strW[] = { 0 };
196 static const WCHAR CodepageW[] = {'C','o','d','e','p','a','g','e',0};
197 static const WCHAR LabelW[] = {'L','a','b','e','l',0};
198 static const WCHAR SerialW[] = {'S','e','r','i','a','l',0};
199 static const WCHAR zeroW[] = {'0',0};
200 static const WCHAR def_serialW[] = {'1','2','3','4','5','6','7','8',0};
201 static const WCHAR FilesystemW[] = {'F','i','l','e','s','y','s','t','e','m',0};
202 static const WCHAR win95W[] = {'w','i','n','9','5',0};
203 static const WCHAR DeviceW[] = {'D','e','v','i','c','e',0};
204 static const WCHAR ReadVolInfoW[] = {'R','e','a','d','V','o','l','I','n','f','o',0};
205 static const WCHAR FailReadOnlyW[] = {'F','a','i','l','R','e','a','d','O','n','l','y',0};
206 static const WCHAR driveC_labelW[] = {'D','r','i','v','e',' ','C',' ',' ',' ',' ',0};
208 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
210 PROFILE_GetWineIniString( name, PathW, empty_strW, path, MAX_PATHNAME_LEN );
211 if (path[0])
213 /* Get the code page number */
214 PROFILE_GetWineIniString( name, CodepageW, zeroW, /* 0 == CP_ACP */
215 buffer, 80 );
216 drive->codepage = strtolW( buffer, NULL, 10 );
218 p = path + strlenW(path) - 1;
219 while ((p > path) && (*p == '/')) *p-- = '\0';
221 if (path[0] == '/')
223 len = WideCharToMultiByte(drive->codepage, 0, path, -1, NULL, 0, NULL, NULL);
224 drive->root = HeapAlloc(GetProcessHeap(), 0, len);
225 WideCharToMultiByte(drive->codepage, 0, path, -1, drive->root, len, NULL, NULL);
227 else
229 /* relative paths are relative to config dir */
230 const char *config = wine_get_config_dir();
231 len = strlen(config);
232 len += WideCharToMultiByte(drive->codepage, 0, path, -1, NULL, 0, NULL, NULL) + 2;
233 drive->root = HeapAlloc( GetProcessHeap(), 0, len );
234 len -= sprintf( drive->root, "%s/", config );
235 WideCharToMultiByte(drive->codepage, 0, path, -1, drive->root + strlen(drive->root), len, NULL, NULL);
238 if (stat( drive->root, &drive_stat_buffer ))
240 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
241 drive->root, strerror(errno), 'A' + i);
242 HeapFree( GetProcessHeap(), 0, drive->root );
243 drive->root = NULL;
244 continue;
246 if (!S_ISDIR(drive_stat_buffer.st_mode))
248 MESSAGE("%s is not a directory, ignoring drive %c:\n",
249 drive->root, 'A' + i );
250 HeapFree( GetProcessHeap(), 0, drive->root );
251 drive->root = NULL;
252 continue;
255 drive->dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(drive->dos_cwd[0]));
256 drive->unix_cwd = heap_strdup( "" );
257 drive->type = DRIVE_GetDriveType( name );
258 drive->device = NULL;
259 drive->flags = 0;
260 drive->dev = drive_stat_buffer.st_dev;
261 drive->ino = drive_stat_buffer.st_ino;
263 /* Get the drive label */
264 PROFILE_GetWineIniString( name, LabelW, empty_strW, drive->label_conf, 12 );
265 if ((len = strlenW(drive->label_conf)) < 11)
267 /* Pad label with spaces */
268 while(len < 11) drive->label_conf[len++] = ' ';
269 drive->label_conf[11] = '\0';
272 /* Get the serial number */
273 PROFILE_GetWineIniString( name, SerialW, def_serialW, buffer, 80 );
274 drive->serial_conf = strtoulW( buffer, NULL, 16 );
276 /* Get the filesystem type */
277 PROFILE_GetWineIniString( name, FilesystemW, win95W, buffer, 80 );
278 drive->flags = DRIVE_GetFSFlags( name, buffer );
280 /* Get the device */
281 PROFILE_GetWineIniString( name, DeviceW, empty_strW, buffer, 80 );
282 if (buffer[0])
284 int cd_fd;
285 len = WideCharToMultiByte(CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL);
286 drive->device = HeapAlloc(GetProcessHeap(), 0, len);
287 WideCharToMultiByte(drive->codepage, 0, buffer, -1, drive->device, len, NULL, NULL);
289 if (PROFILE_GetWineIniBool( name, ReadVolInfoW, 1))
290 drive->flags |= DRIVE_READ_VOL_INFO;
292 if (drive->type == DRIVE_CDROM)
294 if ((cd_fd = open(drive->device, O_RDONLY|O_NONBLOCK)) != -1)
296 CDROM_InitRegistry(cd_fd);
297 close(cd_fd);
302 /* Get the FailReadOnly flag */
303 if (PROFILE_GetWineIniBool( name, FailReadOnlyW, 0 ))
304 drive->flags |= DRIVE_FAIL_READ_ONLY;
306 /* Make the first hard disk the current drive */
307 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
308 DRIVE_CurDrive = i;
310 count++;
311 TRACE("%s: path=%s type=%s label=%s serial=%08lx "
312 "flags=%08x codepage=%u dev=%x ino=%x\n",
313 debugstr_w(name), drive->root, debugstr_w(DRIVE_Types[drive->type]),
314 debugstr_w(drive->label_conf), drive->serial_conf, drive->flags,
315 drive->codepage, (int)drive->dev, (int)drive->ino );
317 else WARN("%s: not defined\n", debugstr_w(name) );
320 if (!count)
322 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
323 /* Create a C drive pointing to Unix root dir */
324 DOSDrives[2].root = heap_strdup( "/" );
325 DOSDrives[2].dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DOSDrives[2].dos_cwd[0]));
326 DOSDrives[2].unix_cwd = heap_strdup( "" );
327 strcpyW( DOSDrives[2].label_conf, driveC_labelW );
328 DOSDrives[2].serial_conf = 12345678;
329 DOSDrives[2].type = DRIVE_FIXED;
330 DOSDrives[2].device = NULL;
331 DOSDrives[2].flags = 0;
332 DRIVE_CurDrive = 2;
335 /* Make sure the current drive is valid */
336 if (DRIVE_CurDrive == -1)
338 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
340 if (drive->root && !(drive->flags & DRIVE_DISABLED))
342 DRIVE_CurDrive = i;
343 break;
348 /* get current working directory info for all drives */
349 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
351 if (!GetEnvironmentVariableW(drive_env, path, MAX_PATHNAME_LEN)) continue;
352 /* sanity check */
353 if (toupperW(path[0]) != drive_env[1] || path[1] != ':') continue;
354 DRIVE_Chdir( i, path + 2 );
356 return 1;
360 /***********************************************************************
361 * DRIVE_IsValid
363 int DRIVE_IsValid( int drive )
365 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
366 return (DOSDrives[drive].root &&
367 !(DOSDrives[drive].flags & DRIVE_DISABLED));
371 /***********************************************************************
372 * DRIVE_GetCurrentDrive
374 int DRIVE_GetCurrentDrive(void)
376 TDB *pTask = TASK_GetCurrent();
377 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
378 return DRIVE_CurDrive;
382 /***********************************************************************
383 * DRIVE_SetCurrentDrive
385 int DRIVE_SetCurrentDrive( int drive )
387 TDB *pTask = TASK_GetCurrent();
388 if (!DRIVE_IsValid( drive ))
390 SetLastError( ERROR_INVALID_DRIVE );
391 return 0;
393 TRACE("%c:\n", 'A' + drive );
394 DRIVE_CurDrive = drive;
395 if (pTask) pTask->curdrive = drive | 0x80;
396 return 1;
400 /***********************************************************************
401 * DRIVE_FindDriveRoot
403 * Find a drive for which the root matches the beginning of the given path.
404 * This can be used to translate a Unix path into a drive + DOS path.
405 * Return value is the drive, or -1 on error. On success, path is modified
406 * to point to the beginning of the DOS path.
408 * Note: path must be in the encoding of the underlying Unix file system.
410 int DRIVE_FindDriveRoot( const char **path )
412 /* Starting with the full path, check if the device and inode match any of
413 * the wine 'drives'. If not then remove the last path component and try
414 * again. If the last component was a '..' then skip a normal component
415 * since it's a directory that's ascended back out of.
417 int drive, level, len;
418 char buffer[MAX_PATHNAME_LEN];
419 char *p;
420 struct stat st;
422 strcpy( buffer, *path );
423 while ((p = strchr( buffer, '\\' )) != NULL)
424 *p = '/';
425 len = strlen(buffer);
427 /* strip off trailing slashes */
428 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
430 for (;;)
432 /* Find the drive */
433 if (stat( buffer, &st ) == 0 && S_ISDIR( st.st_mode ))
435 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
437 if (!DOSDrives[drive].root ||
438 (DOSDrives[drive].flags & DRIVE_DISABLED))
439 continue;
441 if ((DOSDrives[drive].dev == st.st_dev) &&
442 (DOSDrives[drive].ino == st.st_ino))
444 if (len == 1) len = 0; /* preserve root slash in returned path */
445 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
446 *path, 'A' + drive, buffer, *path + len);
447 *path += len;
448 if (!**path) *path = "\\";
449 return drive;
453 if (len <= 1) return -1; /* reached root */
455 level = 0;
456 while (level < 1)
458 /* find start of the last path component */
459 while (len > 1 && buffer[len - 1] != '/') len--;
460 if (!buffer[len]) break; /* empty component -> reached root */
461 /* does removing it take us up a level? */
462 if (strcmp( buffer + len, "." ) != 0)
463 level += strcmp( buffer + len, ".." ) ? 1 : -1;
464 buffer[len] = 0;
465 /* strip off trailing slashes */
466 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
472 /***********************************************************************
473 * DRIVE_FindDriveRootW
475 * Unicode version of DRIVE_FindDriveRoot.
477 int DRIVE_FindDriveRootW( LPCWSTR *path )
479 int drive, level, len;
480 WCHAR buffer[MAX_PATHNAME_LEN];
481 WCHAR *p;
482 struct stat st;
484 strcpyW( buffer, *path );
485 while ((p = strchrW( buffer, '\\' )) != NULL)
486 *p = '/';
487 len = strlenW(buffer);
489 /* strip off trailing slashes */
490 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
492 for (;;)
494 int codepage = -1;
496 /* Find the drive */
497 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
499 char buffA[MAX_PATHNAME_LEN];
501 if (!DOSDrives[drive].root ||
502 (DOSDrives[drive].flags & DRIVE_DISABLED))
503 continue;
505 if (codepage != DOSDrives[drive].codepage)
507 WideCharToMultiByte( DOSDrives[drive].codepage, 0, buffer, -1,
508 buffA, sizeof(buffA), NULL, NULL );
509 if (stat( buffA, &st ) == -1 || !S_ISDIR( st.st_mode ))
511 codepage = -1;
512 continue;
514 codepage = DOSDrives[drive].codepage;
517 if ((DOSDrives[drive].dev == st.st_dev) &&
518 (DOSDrives[drive].ino == st.st_ino))
520 static const WCHAR rootW[] = {'\\',0};
522 if (len == 1) len = 0; /* preserve root slash in returned path */
523 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
524 debugstr_w(*path), 'A' + drive, debugstr_w(buffer), debugstr_w(*path + len));
525 *path += len;
526 if (!**path) *path = rootW;
527 return drive;
530 if (len <= 1) return -1; /* reached root */
532 level = 0;
533 while (level < 1)
535 static const WCHAR dotW[] = {'.',0};
536 static const WCHAR dotdotW[] = {'.','.',0};
538 /* find start of the last path component */
539 while (len > 1 && buffer[len - 1] != '/') len--;
540 if (!buffer[len]) break; /* empty component -> reached root */
541 /* does removing it take us up a level? */
542 if (strcmpW( buffer + len, dotW ) != 0)
543 level += strcmpW( buffer + len, dotdotW ) ? 1 : -1;
544 buffer[len] = 0;
545 /* strip off trailing slashes */
546 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
552 /***********************************************************************
553 * DRIVE_GetRoot
555 const char * DRIVE_GetRoot( int drive )
557 if (!DRIVE_IsValid( drive )) return NULL;
558 return DOSDrives[drive].root;
562 /***********************************************************************
563 * DRIVE_GetDosCwd
565 LPCWSTR DRIVE_GetDosCwd( int drive )
567 TDB *pTask = TASK_GetCurrent();
568 if (!DRIVE_IsValid( drive )) return NULL;
570 /* Check if we need to change the directory to the new task. */
571 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
572 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
573 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
575 static const WCHAR rootW[] = {'\\',0};
576 WCHAR curdirW[MAX_PATH];
577 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
578 /* Perform the task-switch */
579 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
580 DRIVE_LastTask = GetCurrentTask();
582 return DOSDrives[drive].dos_cwd;
586 /***********************************************************************
587 * DRIVE_GetUnixCwd
589 const char * DRIVE_GetUnixCwd( int drive )
591 TDB *pTask = TASK_GetCurrent();
592 if (!DRIVE_IsValid( drive )) return NULL;
594 /* Check if we need to change the directory to the new task. */
595 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
596 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
597 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
599 static const WCHAR rootW[] = {'\\',0};
600 WCHAR curdirW[MAX_PATH];
601 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
602 /* Perform the task-switch */
603 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
604 DRIVE_LastTask = GetCurrentTask();
606 return DOSDrives[drive].unix_cwd;
610 /***********************************************************************
611 * DRIVE_GetDevice
613 const char * DRIVE_GetDevice( int drive )
615 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
618 /******************************************************************
619 * static WORD CDROM_Data_FindBestVoldesc
623 static WORD CDROM_Data_FindBestVoldesc(int fd)
625 BYTE cur_vd_type, max_vd_type = 0;
626 unsigned int offs, best_offs = 0, extra_offs = 0;
627 char sig[3];
629 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
631 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
632 * the volume label is displaced forward by 8
634 lseek(fd, offs + 11, SEEK_SET); /* check for non-ISO9660 signature */
635 read(fd, &sig, 3);
636 if ((sig[0] == 'R') && (sig[1] == 'O') && (sig[2]=='M'))
638 extra_offs = 8;
640 lseek(fd, offs + extra_offs, SEEK_SET);
641 read(fd, &cur_vd_type, 1);
642 if (cur_vd_type == 0xff) /* voldesc set terminator */
643 break;
644 if (cur_vd_type > max_vd_type)
646 max_vd_type = cur_vd_type;
647 best_offs = offs + extra_offs;
650 return best_offs;
653 /***********************************************************************
654 * DRIVE_ReadSuperblock
656 * NOTE
657 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
658 * to check, that they are writing on a FAT filesystem !
660 int DRIVE_ReadSuperblock (int drive, char * buff)
662 #define DRIVE_SUPER 96
663 int fd;
664 off_t offs;
665 int ret = 0;
666 struct stat stat_buf;
668 memset(buff, 0, DRIVE_SUPER);
669 /* O_NONBLOCK in case we're opening FIFO; will be reset later */
670 if ((fd = open(DOSDrives[drive].device, O_RDONLY|O_NOCTTY|O_NONBLOCK)) != -1) {
671 if (fstat(fd, &stat_buf) < 0) { /* shouldn't happen since we just opened that file */
672 ERR("fstat() failed for opened device '%s' (drive %c:) ! IT SHOULDN'T HAPPEN !!!\n",
673 DOSDrives[drive].device, 'A'+drive);
674 ret = -1;
675 } else if (!S_ISBLK(stat_buf.st_mode)) {
676 ERR("Device '%s' (drive %c:) is not a block device - check your config\n",
677 DOSDrives[drive].device, 'A'+drive);
678 ret = -1;
679 /* reset O_NONBLOCK */
680 } else if (fcntl(fd, F_SETFL, 0) < 0 || fcntl(fd, F_GETFL) & O_NONBLOCK) {
681 ERR("fcntl() failed to reset O_NONBLOCK for device '%s' (drive %c:)\n",
682 DOSDrives[drive].device, 'A'+drive);
683 ret = -1;
685 if (ret) {
686 close(fd);
687 fd = -1;
689 } else {
690 if (!DOSDrives[drive].device)
691 ERR("No device configured for drive %c: !\n", 'A'+drive);
692 else
693 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
694 (stat(DOSDrives[drive].device, &stat_buf)) ?
695 "not available or symlink not valid ?" : "no permission");
697 if (fd == -1) {
698 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
699 PROFILE_UsageWineIni();
700 return -1;
703 switch(DOSDrives[drive].type)
705 case DRIVE_REMOVABLE:
706 case DRIVE_FIXED:
707 offs = 0;
708 break;
709 case DRIVE_CDROM:
710 offs = CDROM_Data_FindBestVoldesc(fd);
711 break;
712 default:
713 offs = 0;
714 break;
717 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs))
719 ret = -4;
720 goto the_end;
722 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER)
724 ret = -2;
725 goto the_end;
728 switch(DOSDrives[drive].type)
730 case DRIVE_REMOVABLE:
731 case DRIVE_FIXED:
732 if ((buff[0x26]!=0x29) || /* Check for FAT present */
733 /* FIXME: do really all FAT have their name beginning with
734 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
735 memcmp( buff+0x36,"FAT",3))
737 ERR("The filesystem is not FAT !! (device=%s)\n",
738 DOSDrives[drive].device);
739 ret = -3;
740 goto the_end;
742 break;
743 case DRIVE_CDROM:
744 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
746 ret = -3;
747 goto the_end;
749 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
750 break;
751 default:
752 ret = -3;
753 goto the_end;
756 return close(fd);
757 the_end:
758 close(fd);
759 return ret;
763 /***********************************************************************
764 * DRIVE_WriteSuperblockEntry
766 * NOTE
767 * We are writing as little as possible (ie. not the whole SuperBlock)
768 * not to interfere with kernel. The drive can be mounted !
770 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
772 int fd;
774 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
776 ERR("Cannot open the device %s (for writing)\n",
777 DOSDrives[drive].device);
778 return -1;
780 if (lseek(fd,ofs,SEEK_SET)!=ofs)
782 ERR("lseek failed on device %s !\n",
783 DOSDrives[drive].device);
784 close(fd);
785 return -2;
787 if (write(fd,buff,len)!=len)
789 close(fd);
790 ERR("Cannot write on %s !\n",
791 DOSDrives[drive].device);
792 return -3;
794 return close (fd);
797 /******************************************************************
798 * static HANDLE CDROM_Open
802 static HANDLE CDROM_Open(int drive)
804 WCHAR root[] = {'\\','\\','.','\\','A',':',0};
805 root[4] += drive;
806 return CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
809 /**************************************************************************
810 * CDROM_Data_GetLabel [internal]
812 DWORD CDROM_Data_GetLabel(int drive, WCHAR *label)
814 #define LABEL_LEN 32+1
815 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
816 WORD offs = CDROM_Data_FindBestVoldesc(dev);
817 WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
818 DWORD unicode_id = 0;
820 if (offs)
822 if ((lseek(dev, offs+0x58, SEEK_SET) == offs+0x58)
823 && (read(dev, &unicode_id, 3) == 3))
825 int ver = (unicode_id & 0xff0000) >> 16;
827 if ((lseek(dev, offs+0x28, SEEK_SET) != offs+0x28)
828 || (read(dev, &label_read, LABEL_LEN) != LABEL_LEN))
829 goto failure;
831 close(dev);
832 if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
833 && ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
834 { /* yippee, unicode */
835 int i;
836 WORD ch;
837 for (i=0; i<LABEL_LEN;i++)
838 { /* Motorola -> Intel Unicode conversion :-\ */
839 ch = label_read[i];
840 label_read[i] = (ch << 8) | (ch >> 8);
842 strncpyW(label, label_read, 11);
843 label[11] = 0;
845 else
847 MultiByteToWideChar(DOSDrives[drive].codepage, 0, (LPSTR)label_read, -1, label, 11);
848 label[11] = '\0';
850 return 1;
853 failure:
854 close(dev);
855 ERR("error reading label !\n");
856 return 0;
859 /**************************************************************************
860 * CDROM_GetLabel [internal]
862 static DWORD CDROM_GetLabel(int drive, WCHAR *label)
864 HANDLE h = CDROM_Open(drive);
865 CDROM_DISK_DATA cdd;
866 DWORD br;
867 DWORD ret = 1;
869 if (!h || !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
870 return 0;
872 switch (cdd.DiskData & 0x03)
874 case CDROM_DISK_DATA_TRACK:
875 if (!CDROM_Data_GetLabel(drive, label))
876 ret = 0;
877 break;
878 case CDROM_DISK_AUDIO_TRACK:
880 static const WCHAR audioCD[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
881 strcpyW(label, audioCD);
882 break;
884 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
885 FIXME("Need to get the label of a mixed mode CD!\n");
886 /* This assumes that the first track is a data track! */
887 /* I guess the correct way would be to enumerate all data tracks
888 and check each for the title */
889 if (!CDROM_Data_GetLabel(drive, label))
890 ret = 0;
891 break;
892 case 0:
893 ret = 0;
894 break;
896 TRACE("CD: label is %s\n", debugstr_w(label));
898 return ret;
900 /***********************************************************************
901 * DRIVE_GetLabel
903 LPCWSTR DRIVE_GetLabel( int drive )
905 int read = 0;
906 char buff[DRIVE_SUPER];
907 int offs = -1;
909 if (!DRIVE_IsValid( drive )) return NULL;
910 if (DOSDrives[drive].type == DRIVE_CDROM)
912 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
914 else
915 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
917 if (DRIVE_ReadSuperblock(drive,(char *) buff))
918 ERR("Invalid or unreadable superblock on %s (%c:).\n",
919 DOSDrives[drive].device, (char)(drive+'A'));
920 else {
921 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
922 DOSDrives[drive].type == DRIVE_FIXED)
923 offs = 0x2b;
925 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
926 if (offs != -1)
927 MultiByteToWideChar(DOSDrives[drive].codepage, 0, buff+offs, 11,
928 DOSDrives[drive].label_read, 11);
929 DOSDrives[drive].label_read[11]='\0';
930 read = 1;
934 return (read) ?
935 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
938 #define CDFRAMES_PERSEC 75
939 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
940 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
941 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
943 /**************************************************************************
944 * CDROM_Audio_GetSerial [internal]
946 static DWORD CDROM_Audio_GetSerial(HANDLE h)
948 unsigned long serial = 0;
949 int i;
950 WORD wMagic;
951 DWORD dwStart, dwEnd, br;
952 CDROM_TOC toc;
954 if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
955 return 0;
958 * wMagic collects the wFrames from track 1
959 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
960 * frames.
961 * There it is collected for correcting the serial when there are less than
962 * 3 tracks.
964 wMagic = toc.TrackData[0].Address[2];
965 dwStart = FRAME_OF_TOC(toc, toc.FirstTrack);
967 for (i = 0; i <= toc.LastTrack - toc.FirstTrack; i++) {
968 serial += (toc.TrackData[i].Address[0] << 16) |
969 (toc.TrackData[i].Address[1] << 8) | toc.TrackData[i].Address[2];
971 dwEnd = FRAME_OF_TOC(toc, toc.LastTrack + 1);
973 if (toc.LastTrack - toc.FirstTrack + 1 < 3)
974 serial += wMagic + (dwEnd - dwStart);
976 return serial;
979 /**************************************************************************
980 * CDROM_Data_GetSerial [internal]
982 static DWORD CDROM_Data_GetSerial(int drive)
984 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
985 WORD offs;
986 union {
987 unsigned long val;
988 unsigned char p[4];
989 } serial;
990 BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
993 if (dev == -1) return 0;
994 offs = CDROM_Data_FindBestVoldesc(dev);
996 serial.val = 0;
997 if (offs)
999 BYTE buf[2048];
1000 OSVERSIONINFOA ovi;
1001 int i;
1003 lseek(dev, offs, SEEK_SET);
1004 read(dev, buf, 2048);
1006 * OK, another braindead one... argh. Just believe it.
1007 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
1008 * It's true and nobody will ever be able to change it.
1010 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1011 GetVersionExA(&ovi);
1012 if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (ovi.dwMajorVersion >= 4))
1014 b0 = 3; b1 = 2; b2 = 1; b3 = 0;
1016 for (i = 0; i < 2048; i += 4)
1018 /* DON'T optimize this into DWORD !! (breaks overflow) */
1019 serial.p[b0] += buf[i+b0];
1020 serial.p[b1] += buf[i+b1];
1021 serial.p[b2] += buf[i+b2];
1022 serial.p[b3] += buf[i+b3];
1025 close(dev);
1026 return serial.val;
1029 /**************************************************************************
1030 * CDROM_GetSerial [internal]
1032 static DWORD CDROM_GetSerial(int drive)
1034 DWORD serial = 0;
1035 HANDLE h = CDROM_Open(drive);
1036 CDROM_DISK_DATA cdd;
1037 DWORD br;
1039 if (!h || ! !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
1040 return 0;
1042 switch (cdd.DiskData & 0x03)
1044 case CDROM_DISK_DATA_TRACK:
1045 /* hopefully a data CD */
1046 serial = CDROM_Data_GetSerial(drive);
1047 break;
1048 case CDROM_DISK_AUDIO_TRACK:
1049 /* fall thru */
1050 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
1051 serial = CDROM_Audio_GetSerial(h);
1052 break;
1053 case 0:
1054 break;
1057 if (serial)
1058 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
1060 CloseHandle(h);
1062 return serial;
1065 /***********************************************************************
1066 * DRIVE_GetSerialNumber
1068 DWORD DRIVE_GetSerialNumber( int drive )
1070 DWORD serial = 0;
1071 char buff[DRIVE_SUPER];
1073 TRACE("drive %d, type = %d\n", drive, DOSDrives[drive].type);
1075 if (!DRIVE_IsValid( drive )) return 0;
1077 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1079 switch(DOSDrives[drive].type)
1081 case DRIVE_REMOVABLE:
1082 case DRIVE_FIXED:
1083 if (DRIVE_ReadSuperblock(drive,(char *) buff))
1084 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
1085 " Maybe not FAT?\n" ,
1086 DOSDrives[drive].device, 'A'+drive);
1087 else
1088 serial = *((DWORD*)(buff+0x27));
1089 break;
1090 case DRIVE_CDROM:
1091 serial = CDROM_GetSerial(drive);
1092 break;
1093 default:
1094 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
1098 return (serial) ? serial : DOSDrives[drive].serial_conf;
1102 /***********************************************************************
1103 * DRIVE_SetSerialNumber
1105 int DRIVE_SetSerialNumber( int drive, DWORD serial )
1107 char buff[DRIVE_SUPER];
1109 if (!DRIVE_IsValid( drive )) return 0;
1111 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1113 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
1114 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
1115 /* check, if the drive has a FAT filesystem */
1116 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
1117 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
1118 return 1;
1121 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
1122 DOSDrives[drive].serial_conf = serial;
1123 return 1;
1127 /***********************************************************************
1128 * DRIVE_GetType
1130 static UINT DRIVE_GetType( int drive )
1132 if (!DRIVE_IsValid( drive )) return DRIVE_NO_ROOT_DIR;
1133 return DOSDrives[drive].type;
1137 /***********************************************************************
1138 * DRIVE_GetFlags
1140 UINT DRIVE_GetFlags( int drive )
1142 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1143 return DOSDrives[drive].flags;
1146 /***********************************************************************
1147 * DRIVE_GetCodepage
1149 UINT DRIVE_GetCodepage( int drive )
1151 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1152 return DOSDrives[drive].codepage;
1156 /***********************************************************************
1157 * DRIVE_Chdir
1159 int DRIVE_Chdir( int drive, LPCWSTR path )
1161 DOS_FULL_NAME full_name;
1162 WCHAR buffer[MAX_PATHNAME_LEN];
1163 LPSTR unix_cwd;
1164 BY_HANDLE_FILE_INFORMATION info;
1165 TDB *pTask = TASK_GetCurrent();
1167 buffer[0] = 'A' + drive;
1168 buffer[1] = ':';
1169 buffer[2] = 0;
1170 TRACE("(%s,%s)\n", debugstr_w(buffer), debugstr_w(path) );
1171 strncpyW( buffer + 2, path, MAX_PATHNAME_LEN - 2 );
1172 buffer[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1174 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
1175 if (!FILE_Stat( full_name.long_name, &info, NULL )) return 0;
1176 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1178 SetLastError( ERROR_FILE_NOT_FOUND );
1179 return 0;
1181 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
1182 while (*unix_cwd == '/') unix_cwd++;
1184 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1185 'A' + drive, unix_cwd, debugstr_w(full_name.short_name + 3) );
1187 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
1188 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
1189 DOSDrives[drive].dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name.short_name) - 2) * sizeof(WCHAR));
1190 strcpyW(DOSDrives[drive].dos_cwd, full_name.short_name + 3);
1191 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
1193 if (pTask && (pTask->curdrive & 0x80) &&
1194 ((pTask->curdrive & ~0x80) == drive))
1196 WideCharToMultiByte(CP_ACP, 0, full_name.short_name + 2, -1,
1197 pTask->curdir, sizeof(pTask->curdir), NULL, NULL);
1198 DRIVE_LastTask = GetCurrentTask();
1200 return 1;
1204 /***********************************************************************
1205 * DRIVE_Disable
1207 int DRIVE_Disable( int drive )
1209 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1211 SetLastError( ERROR_INVALID_DRIVE );
1212 return 0;
1214 DOSDrives[drive].flags |= DRIVE_DISABLED;
1215 return 1;
1219 /***********************************************************************
1220 * DRIVE_Enable
1222 int DRIVE_Enable( int drive )
1224 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1226 SetLastError( ERROR_INVALID_DRIVE );
1227 return 0;
1229 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1230 return 1;
1234 /***********************************************************************
1235 * DRIVE_SetLogicalMapping
1237 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
1239 /* If new_drive is already valid, do nothing and return 0
1240 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1242 DOSDRIVE *old, *new;
1244 old = DOSDrives + existing_drive;
1245 new = DOSDrives + new_drive;
1247 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
1248 !old->root ||
1249 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
1251 SetLastError( ERROR_INVALID_DRIVE );
1252 return 0;
1255 if ( new->root )
1257 TRACE("Can't map drive %c: to already existing drive %c:\n",
1258 'A' + existing_drive, 'A' + new_drive );
1259 /* it is already mapped there, so return success */
1260 if (!strcmp(old->root,new->root))
1261 return 1;
1262 return 0;
1265 new->root = heap_strdup( old->root );
1266 new->dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(old->dos_cwd) + 1) * sizeof(WCHAR));
1267 strcpyW(new->dos_cwd, old->dos_cwd);
1268 new->unix_cwd = heap_strdup( old->unix_cwd );
1269 new->device = heap_strdup( old->device );
1270 memcpy ( new->label_conf, old->label_conf, 12 );
1271 memcpy ( new->label_read, old->label_read, 12 );
1272 new->serial_conf = old->serial_conf;
1273 new->type = old->type;
1274 new->flags = old->flags;
1275 new->dev = old->dev;
1276 new->ino = old->ino;
1278 TRACE("Drive %c: is now equal to drive %c:\n",
1279 'A' + new_drive, 'A' + existing_drive );
1281 return 1;
1285 /***********************************************************************
1286 * DRIVE_OpenDevice
1288 * Open the drive raw device and return a Unix fd (or -1 on error).
1290 int DRIVE_OpenDevice( int drive, int flags )
1292 if (!DRIVE_IsValid( drive )) return -1;
1293 return open( DOSDrives[drive].device, flags );
1297 /***********************************************************************
1298 * DRIVE_RawRead
1300 * Read raw sectors from a device
1302 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1304 int fd;
1306 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1308 lseek( fd, begin * 512, SEEK_SET );
1309 /* FIXME: check errors */
1310 read( fd, dataptr, nr_sect * 512 );
1311 close( fd );
1313 else
1315 memset(dataptr, 0, nr_sect * 512);
1316 if (fake_success)
1318 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
1319 if (begin == 1) *dataptr = 0xf8;
1321 else
1322 return 0;
1324 return 1;
1328 /***********************************************************************
1329 * DRIVE_RawWrite
1331 * Write raw sectors to a device
1333 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1335 int fd;
1337 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1339 lseek( fd, begin * 512, SEEK_SET );
1340 /* FIXME: check errors */
1341 write( fd, dataptr, nr_sect * 512 );
1342 close( fd );
1344 else
1345 if (!(fake_success))
1346 return 0;
1348 return 1;
1352 /***********************************************************************
1353 * DRIVE_GetFreeSpace
1355 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
1356 PULARGE_INTEGER available )
1358 struct statfs info;
1360 if (!DRIVE_IsValid(drive))
1362 SetLastError( ERROR_PATH_NOT_FOUND );
1363 return 0;
1366 /* FIXME: add autoconf check for this */
1367 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1368 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
1369 #else
1370 if (statfs( DOSDrives[drive].root, &info) < 0)
1371 #endif
1373 FILE_SetDosError();
1374 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
1375 return 0;
1378 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
1379 #ifdef HAVE_STRUCT_STATFS_F_BAVAIL
1380 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
1381 #else
1382 # ifdef HAVE_STRUCT_STATFS_F_BFREE
1383 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
1384 # else
1385 # error "statfs has no bfree/bavail member!"
1386 # endif
1387 #endif
1388 if (DOSDrives[drive].type == DRIVE_CDROM)
1389 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1390 available->QuadPart = 0;
1392 return 1;
1395 /***********************************************************************
1396 * DRIVE_GetCurrentDirectory
1397 * Returns "X:\\path\\etc\\".
1399 * Despite the API description, return required length including the
1400 * terminating null when buffer too small. This is the real behaviour.
1402 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPWSTR buf )
1404 UINT ret;
1405 LPCWSTR dos_cwd = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1406 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1408 ret = strlenW(dos_cwd) + 3; /* length of WHOLE current directory */
1409 if (ret >= buflen) return ret + 1;
1411 strcpyW( buf, driveA_rootW );
1412 buf[0] += DRIVE_GetCurrentDrive();
1413 strcatW( buf, dos_cwd );
1414 return ret;
1418 /***********************************************************************
1419 * DRIVE_BuildEnv
1421 * Build the environment array containing the drives' current directories.
1422 * Resulting pointer must be freed with HeapFree.
1424 char *DRIVE_BuildEnv(void)
1426 int i, length = 0;
1427 LPCWSTR cwd[MAX_DOS_DRIVES];
1428 char *env, *p;
1430 for (i = 0; i < MAX_DOS_DRIVES; i++)
1432 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0])
1433 length += WideCharToMultiByte(DRIVE_GetCodepage(i), 0,
1434 cwd[i], -1, NULL, 0, NULL, NULL) + 7;
1436 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
1437 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
1439 if (cwd[i] && cwd[i][0])
1441 *p++ = '='; *p++ = 'A' + i; *p++ = ':';
1442 *p++ = '='; *p++ = 'A' + i; *p++ = ':'; *p++ = '\\';
1443 WideCharToMultiByte(DRIVE_GetCodepage(i), 0, cwd[i], -1, p, 0x7fffffff, NULL, NULL);
1444 p += strlen(p) + 1;
1447 *p = 0;
1448 return env;
1452 /***********************************************************************
1453 * GetDiskFreeSpace (KERNEL.422)
1455 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
1456 LPDWORD sector_bytes, LPDWORD free_clusters,
1457 LPDWORD total_clusters )
1459 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
1460 free_clusters, total_clusters );
1464 /***********************************************************************
1465 * GetDiskFreeSpaceW (KERNEL32.@)
1467 * Fails if expression resulting from current drive's dir and "root"
1468 * is not a root dir of the target drive.
1470 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1471 * if the corresponding info is unneeded.
1473 * FIXME: needs to support UNC names from Win95 OSR2 on.
1475 * Behaviour under Win95a:
1476 * CurrDir root result
1477 * "E:\\TEST" "E:" FALSE
1478 * "E:\\" "E:" TRUE
1479 * "E:\\" "E" FALSE
1480 * "E:\\" "\\" TRUE
1481 * "E:\\TEST" "\\" TRUE
1482 * "E:\\TEST" ":\\" FALSE
1483 * "E:\\TEST" "E:\\" TRUE
1484 * "E:\\TEST" "" FALSE
1485 * "E:\\" "" FALSE (!)
1486 * "E:\\" 0x0 TRUE
1487 * "E:\\TEST" 0x0 TRUE (!)
1488 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1489 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1491 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1492 LPDWORD sector_bytes, LPDWORD free_clusters,
1493 LPDWORD total_clusters )
1495 int drive, sec_size;
1496 ULARGE_INTEGER size,available;
1497 LPCWSTR path;
1498 DWORD cluster_sec;
1500 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root), cluster_sectors, sector_bytes,
1501 free_clusters, total_clusters);
1503 if (!root || root[0] == '\\' || root[0] == '/')
1504 drive = DRIVE_GetCurrentDrive();
1505 else
1506 if (root[0] && root[1] == ':') /* root contains drive tag */
1508 drive = toupperW(root[0]) - 'A';
1509 path = &root[2];
1510 if (path[0] == '\0')
1512 path = DRIVE_GetDosCwd(drive);
1513 if (!path)
1515 SetLastError(ERROR_PATH_NOT_FOUND);
1516 return FALSE;
1519 else
1520 if (path[0] == '\\')
1521 path++;
1523 if (path[0]) /* oops, we are in a subdir */
1525 SetLastError(ERROR_INVALID_NAME);
1526 return FALSE;
1529 else
1531 if (!root[0])
1532 SetLastError(ERROR_PATH_NOT_FOUND);
1533 else
1534 SetLastError(ERROR_INVALID_NAME);
1535 return FALSE;
1538 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1540 /* Cap the size and available at 2GB as per specs. */
1541 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1543 size.s.HighPart = 0;
1544 size.s.LowPart = 0x7fffffff;
1546 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1548 available.s.HighPart =0;
1549 available.s.LowPart = 0x7fffffff;
1551 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1552 size.s.LowPart /= sec_size;
1553 available.s.LowPart /= sec_size;
1554 /* FIXME: probably have to adjust those variables too for CDFS */
1555 cluster_sec = 1;
1556 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1558 if (cluster_sectors)
1559 *cluster_sectors = cluster_sec;
1560 if (sector_bytes)
1561 *sector_bytes = sec_size;
1562 if (free_clusters)
1563 *free_clusters = available.s.LowPart / cluster_sec;
1564 if (total_clusters)
1565 *total_clusters = size.s.LowPart / cluster_sec;
1566 return TRUE;
1570 /***********************************************************************
1571 * GetDiskFreeSpaceA (KERNEL32.@)
1573 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1574 LPDWORD sector_bytes, LPDWORD free_clusters,
1575 LPDWORD total_clusters )
1577 UNICODE_STRING rootW;
1578 BOOL ret = FALSE;
1580 if (root)
1582 if(!RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1584 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1585 return FALSE;
1588 else
1589 rootW.Buffer = NULL;
1591 ret = GetDiskFreeSpaceW(rootW.Buffer, cluster_sectors, sector_bytes,
1592 free_clusters, total_clusters );
1593 RtlFreeUnicodeString(&rootW);
1595 return ret;
1599 /***********************************************************************
1600 * GetDiskFreeSpaceExW (KERNEL32.@)
1602 * This function is used to acquire the size of the available and
1603 * total space on a logical volume.
1605 * RETURNS
1607 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1608 * detailed error information.
1611 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root,
1612 PULARGE_INTEGER avail,
1613 PULARGE_INTEGER total,
1614 PULARGE_INTEGER totalfree)
1616 int drive;
1617 ULARGE_INTEGER size,available;
1619 if (!root) drive = DRIVE_GetCurrentDrive();
1620 else
1621 { /* C: always works for GetDiskFreeSpaceEx */
1622 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
1624 FIXME("there are valid root names which are not supported yet\n");
1625 /* ..like UNC names, for instance. */
1627 WARN("invalid root '%s'\n", debugstr_w(root));
1628 return FALSE;
1630 drive = toupperW(root[0]) - 'A';
1633 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1635 if (total)
1637 total->s.HighPart = size.s.HighPart;
1638 total->s.LowPart = size.s.LowPart;
1641 if (totalfree)
1643 totalfree->s.HighPart = available.s.HighPart;
1644 totalfree->s.LowPart = available.s.LowPart;
1647 if (avail)
1649 if (FIXME_ON(dosfs))
1651 /* On Windows2000, we need to check the disk quota
1652 allocated for the user owning the calling process. We
1653 don't want to be more obtrusive than necessary with the
1654 FIXME messages, so don't print the FIXME unless Wine is
1655 actually masquerading as Windows2000. */
1657 OSVERSIONINFOA ovi;
1658 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1659 if (GetVersionExA(&ovi))
1661 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1662 FIXME("no per-user quota support yet\n");
1666 /* Quick hack, should eventually be fixed to work 100% with
1667 Windows2000 (see comment above). */
1668 avail->s.HighPart = available.s.HighPart;
1669 avail->s.LowPart = available.s.LowPart;
1672 return TRUE;
1675 /***********************************************************************
1676 * GetDiskFreeSpaceExA (KERNEL32.@)
1678 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
1679 PULARGE_INTEGER total,
1680 PULARGE_INTEGER totalfree)
1682 UNICODE_STRING rootW;
1683 BOOL ret;
1685 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
1686 else rootW.Buffer = NULL;
1688 ret = GetDiskFreeSpaceExW( rootW.Buffer, avail, total, totalfree);
1690 RtlFreeUnicodeString(&rootW);
1691 return ret;
1694 /***********************************************************************
1695 * GetDriveType (KERNEL.136)
1696 * This function returns the type of a drive in Win16.
1697 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1698 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1699 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1700 * do any pseudo-clever changes.
1702 * RETURNS
1703 * drivetype DRIVE_xxx
1705 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1707 UINT type = DRIVE_GetType(drive);
1708 TRACE("(%c:)\n", 'A' + drive );
1709 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1710 return type;
1714 /***********************************************************************
1715 * GetDriveTypeW (KERNEL32.@)
1717 * Returns the type of the disk drive specified. If root is NULL the
1718 * root of the current directory is used.
1720 * RETURNS
1722 * Type of drive (from Win32 SDK):
1724 * DRIVE_UNKNOWN unable to find out anything about the drive
1725 * DRIVE_NO_ROOT_DIR nonexistent root dir
1726 * DRIVE_REMOVABLE the disk can be removed from the machine
1727 * DRIVE_FIXED the disk can not be removed from the machine
1728 * DRIVE_REMOTE network disk
1729 * DRIVE_CDROM CDROM drive
1730 * DRIVE_RAMDISK virtual disk in RAM
1732 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1734 int drive;
1735 TRACE("(%s)\n", debugstr_w(root));
1737 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1738 else
1740 if ((root[1]) && (root[1] != ':'))
1742 WARN("invalid root %s\n", debugstr_w(root));
1743 return DRIVE_NO_ROOT_DIR;
1745 drive = toupperW(root[0]) - 'A';
1747 return DRIVE_GetType(drive);
1751 /***********************************************************************
1752 * GetDriveTypeA (KERNEL32.@)
1754 UINT WINAPI GetDriveTypeA( LPCSTR root )
1756 UNICODE_STRING rootW;
1757 UINT ret = 0;
1759 if (root)
1761 if( !RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1763 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1764 return 0;
1767 else
1768 rootW.Buffer = NULL;
1770 ret = GetDriveTypeW(rootW.Buffer);
1772 RtlFreeUnicodeString(&rootW);
1773 return ret;
1778 /***********************************************************************
1779 * GetCurrentDirectory (KERNEL.411)
1781 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1783 WCHAR cur_dirW[MAX_PATH];
1785 DRIVE_GetCurrentDirectory(MAX_PATH, cur_dirW);
1786 return (UINT16)WideCharToMultiByte(CP_ACP, 0, cur_dirW, -1, buf, buflen, NULL, NULL);
1790 /***********************************************************************
1791 * GetCurrentDirectoryW (KERNEL32.@)
1793 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1795 UINT ret;
1796 WCHAR longname[MAX_PATHNAME_LEN];
1797 WCHAR shortname[MAX_PATHNAME_LEN];
1799 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1800 if ( ret > MAX_PATHNAME_LEN ) {
1801 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1802 return ret;
1804 GetLongPathNameW(shortname, longname, MAX_PATHNAME_LEN);
1805 ret = strlenW( longname ) + 1;
1806 if (ret > buflen) return ret;
1807 strcpyW(buf, longname);
1808 return ret - 1;
1811 /***********************************************************************
1812 * GetCurrentDirectoryA (KERNEL32.@)
1814 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1816 WCHAR bufferW[MAX_PATH];
1817 DWORD ret, retW;
1819 retW = GetCurrentDirectoryW(MAX_PATH, bufferW);
1821 if (!retW)
1822 ret = 0;
1823 else if (retW > MAX_PATH)
1825 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1826 ret = 0;
1828 else
1830 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1831 if (buflen >= ret)
1833 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, buflen, NULL, NULL);
1834 ret--; /* length without 0 */
1837 return ret;
1841 /***********************************************************************
1842 * SetCurrentDirectory (KERNEL.412)
1844 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1846 return SetCurrentDirectoryA( dir );
1850 /***********************************************************************
1851 * SetCurrentDirectoryW (KERNEL32.@)
1853 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dir )
1855 int drive, olddrive = DRIVE_GetCurrentDrive();
1857 if (!dir)
1859 SetLastError(ERROR_INVALID_PARAMETER);
1860 return FALSE;
1862 if (dir[0] && (dir[1]==':'))
1864 drive = toupperW( *dir ) - 'A';
1865 dir += 2;
1867 else
1868 drive = olddrive;
1870 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1871 sets pTask->curdir only if pTask->curdrive is drive */
1872 if (!(DRIVE_SetCurrentDrive( drive )))
1873 return FALSE;
1875 /* FIXME: what about empty strings? Add a \\ ? */
1876 if (!DRIVE_Chdir( drive, dir )) {
1877 DRIVE_SetCurrentDrive(olddrive);
1878 return FALSE;
1880 return TRUE;
1884 /***********************************************************************
1885 * SetCurrentDirectoryA (KERNEL32.@)
1887 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1889 UNICODE_STRING dirW;
1890 BOOL ret = FALSE;
1892 if (!dir)
1894 SetLastError(ERROR_INVALID_PARAMETER);
1895 return FALSE;
1898 if (RtlCreateUnicodeStringFromAsciiz(&dirW, dir))
1900 ret = SetCurrentDirectoryW(dirW.Buffer);
1901 RtlFreeUnicodeString(&dirW);
1903 else
1904 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1905 return ret;
1909 /***********************************************************************
1910 * GetLogicalDriveStringsA (KERNEL32.@)
1912 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1914 int drive, count;
1916 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1917 if (DRIVE_IsValid(drive)) count++;
1918 if ((count * 4) + 1 <= len)
1920 LPSTR p = buffer;
1921 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1922 if (DRIVE_IsValid(drive))
1924 *p++ = 'a' + drive;
1925 *p++ = ':';
1926 *p++ = '\\';
1927 *p++ = '\0';
1929 *p = '\0';
1930 return count * 4;
1932 else
1933 return (count * 4) + 1; /* account for terminating null */
1934 /* The API tells about these different return values */
1938 /***********************************************************************
1939 * GetLogicalDriveStringsW (KERNEL32.@)
1941 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1943 int drive, count;
1945 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1946 if (DRIVE_IsValid(drive)) count++;
1947 if (count * 4 * sizeof(WCHAR) <= len)
1949 LPWSTR p = buffer;
1950 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1951 if (DRIVE_IsValid(drive))
1953 *p++ = (WCHAR)('a' + drive);
1954 *p++ = (WCHAR)':';
1955 *p++ = (WCHAR)'\\';
1956 *p++ = (WCHAR)'\0';
1958 *p = (WCHAR)'\0';
1960 return count * 4 * sizeof(WCHAR);
1964 /***********************************************************************
1965 * GetLogicalDrives (KERNEL32.@)
1967 DWORD WINAPI GetLogicalDrives(void)
1969 DWORD ret = 0;
1970 int drive;
1972 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1974 if ( (DRIVE_IsValid(drive)) ||
1975 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1976 ret |= (1 << drive);
1978 return ret;
1982 /***********************************************************************
1983 * GetVolumeInformationW (KERNEL32.@)
1985 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1986 DWORD label_len, DWORD *serial,
1987 DWORD *filename_len, DWORD *flags,
1988 LPWSTR fsname, DWORD fsname_len )
1990 int drive;
1991 LPWSTR cp;
1993 /* FIXME, SetLastError()s missing */
1995 if (!root) drive = DRIVE_GetCurrentDrive();
1996 else
1998 if (root[0] && root[1] != ':')
2000 WARN("invalid root %s\n", debugstr_w(root));
2001 return FALSE;
2003 drive = toupperW(root[0]) - 'A';
2005 if (!DRIVE_IsValid( drive )) return FALSE;
2006 if (label && label_len)
2008 strncpyW( label, DRIVE_GetLabel(drive), label_len );
2009 label[label_len - 1] = 0; /* ensure 0 termination */
2010 cp = label + strlenW(label);
2011 while (cp != label && *(cp-1) == ' ') cp--;
2012 *cp = '\0';
2014 if (serial) *serial = DRIVE_GetSerialNumber(drive);
2016 /* Set the filesystem information */
2017 /* Note: we only emulate a FAT fs at present */
2019 if (filename_len) {
2020 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
2021 *filename_len = 12;
2022 else
2023 *filename_len = 255;
2025 if (flags)
2027 *flags=0;
2028 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
2029 *flags|=FS_CASE_SENSITIVE;
2030 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
2031 *flags|=FS_CASE_IS_PRESERVED;
2033 if (fsname && fsname_len)
2035 /* Diablo checks that return code ... */
2036 if (DOSDrives[drive].type == DRIVE_CDROM)
2038 static const WCHAR cdfsW[] = {'C','D','F','S',0};
2039 strncpyW( fsname, cdfsW, fsname_len );
2041 else
2043 static const WCHAR fatW[] = {'F','A','T',0};
2044 strncpyW( fsname, fatW, fsname_len );
2046 fsname[fsname_len - 1] = 0; /* ensure 0 termination */
2048 return TRUE;
2052 /***********************************************************************
2053 * GetVolumeInformationA (KERNEL32.@)
2055 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
2056 DWORD label_len, DWORD *serial,
2057 DWORD *filename_len, DWORD *flags,
2058 LPSTR fsname, DWORD fsname_len )
2060 UNICODE_STRING rootW;
2061 LPWSTR labelW, fsnameW;
2062 BOOL ret;
2064 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2065 else rootW.Buffer = NULL;
2066 labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
2067 fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
2069 if ((ret = GetVolumeInformationW(rootW.Buffer, labelW, label_len, serial,
2070 filename_len, flags, fsnameW, fsname_len)))
2072 if (label) WideCharToMultiByte(CP_ACP, 0, labelW, -1, label, label_len, NULL, NULL);
2073 if (fsname) WideCharToMultiByte(CP_ACP, 0, fsnameW, -1, fsname, fsname_len, NULL, NULL);
2076 RtlFreeUnicodeString(&rootW);
2077 if (labelW) HeapFree( GetProcessHeap(), 0, labelW );
2078 if (fsnameW) HeapFree( GetProcessHeap(), 0, fsnameW );
2079 return ret;
2082 /***********************************************************************
2083 * SetVolumeLabelW (KERNEL32.@)
2085 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR volname )
2087 int drive;
2089 /* FIXME, SetLastErrors missing */
2091 if (!root) drive = DRIVE_GetCurrentDrive();
2092 else
2094 if ((root[1]) && (root[1] != ':'))
2096 WARN("invalid root %s\n", debugstr_w(root));
2097 return FALSE;
2099 drive = toupperW(root[0]) - 'A';
2101 if (!DRIVE_IsValid( drive )) return FALSE;
2103 /* some copy protection stuff check this */
2104 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
2106 strncpyW(DOSDrives[drive].label_conf, volname, 12);
2107 DOSDrives[drive].label_conf[12 - 1] = 0; /* ensure 0 termination */
2108 return TRUE;
2111 /***********************************************************************
2112 * SetVolumeLabelA (KERNEL32.@)
2114 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
2116 UNICODE_STRING rootW, volnameW;
2117 BOOL ret;
2119 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2120 else rootW.Buffer = NULL;
2121 if (volname) RtlCreateUnicodeStringFromAsciiz(&volnameW, volname);
2122 else volnameW.Buffer = NULL;
2124 ret = SetVolumeLabelW( rootW.Buffer, volnameW.Buffer );
2126 RtlFreeUnicodeString(&rootW);
2127 RtlFreeUnicodeString(&volnameW);
2128 return ret;
2131 /***********************************************************************
2132 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
2134 DWORD WINAPI GetVolumeNameForVolumeMountPointW(LPWSTR str, DWORD a, DWORD b)
2136 FIXME("(%s, %lx, %lx): stub\n", debugstr_w(str), a, b);
2137 return 0;