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
27 #include "wine/port.h"
34 #include <sys/types.h>
42 #ifdef HAVE_SYS_PARAM_H
43 # include <sys/param.h>
45 #ifdef STATFS_DEFINED_BY_SYS_VFS
48 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
49 # include <sys/mount.h>
51 # ifdef STATFS_DEFINED_BY_SYS_STATFS
52 # include <sys/statfs.h>
57 #define NONAMELESSUNION
58 #define NONAMELESSSTRUCT
61 #include "wine/winbase16.h" /* for GetCurrentTask */
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
);
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 */
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 */
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
},
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
);
141 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
143 extern void CDROM_InitRegistry(int dev
);
145 /***********************************************************************
148 static inline UINT
DRIVE_GetDriveType( INT drive
, LPCWSTR value
)
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
) );
162 /***********************************************************************
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 /***********************************************************************
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
;
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
;
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';
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
);
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
);
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
);
275 drive
->dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(drive
->dos_cwd
[0]));
276 drive
->unix_cwd
= heap_strdup( "" );
277 drive
->device
= NULL
;
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
;
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
)
343 if ((cd_fd
= open(drive
->device
, O_RDONLY
|O_NONBLOCK
)) != -1)
345 CDROM_InitRegistry(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
))
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
);
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;
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
))
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;
408 if (toupperW(path
[0]) != drive_env
[1] || path
[1] != ':') continue;
409 DRIVE_Chdir( i
, path
+ 2 );
415 /***********************************************************************
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
);
448 TRACE("%c:\n", 'A' + drive
);
449 DRIVE_CurDrive
= drive
;
450 if (pTask
) pTask
->curdrive
= drive
| 0x80;
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
];
477 strcpy( buffer
, *path
);
478 while ((p
= strchr( buffer
, '\\' )) != NULL
)
480 len
= strlen(buffer
);
482 /* strip off trailing slashes */
483 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
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
))
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
);
503 if (!**path
) *path
= "\\";
508 if (len
<= 1) return -1; /* reached root */
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;
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
];
539 strcpyW( buffer
, *path
);
540 while ((p
= strchrW( buffer
, '\\' )) != NULL
)
542 len
= strlenW(buffer
);
544 /* strip off trailing slashes */
545 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
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
))
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
))
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
));
581 if (!**path
) *path
= rootW
;
585 if (len
<= 1) return -1; /* reached root */
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;
600 /* strip off trailing slashes */
601 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
607 /***********************************************************************
610 const char * DRIVE_GetRoot( int drive
)
612 if (!DRIVE_IsValid( drive
)) return NULL
;
613 return DOSDrives
[drive
].root
;
617 /***********************************************************************
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 /***********************************************************************
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 /***********************************************************************
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;
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 */
691 if ((sig
[0] == 'R') && (sig
[1] == 'O') && (sig
[2]=='M'))
695 lseek(fd
, offs
+ extra_offs
, SEEK_SET
);
696 read(fd
, &cur_vd_type
, 1);
697 if (cur_vd_type
== 0xff) /* voldesc set terminator */
699 if (cur_vd_type
> max_vd_type
)
701 max_vd_type
= cur_vd_type
;
702 best_offs
= offs
+ extra_offs
;
708 /***********************************************************************
709 * DRIVE_ReadSuperblock
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
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
);
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
);
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
);
745 if (!DOSDrives
[drive
].device
)
746 ERR("No device configured for drive %c: !\n", 'A'+drive
);
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");
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();
758 switch(DOSDrives
[drive
].type
)
760 case DRIVE_REMOVABLE
:
765 offs
= CDROM_Data_FindBestVoldesc(fd
);
772 if ((offs
) && (lseek(fd
,offs
,SEEK_SET
)!=offs
))
777 if (read(fd
,buff
,DRIVE_SUPER
)!=DRIVE_SUPER
)
783 switch(DOSDrives
[drive
].type
)
785 case DRIVE_REMOVABLE
:
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
);
799 if (strncmp(&buff
[1],"CD001",5)) /* Check for iso9660 present */
804 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
818 /***********************************************************************
819 * DRIVE_WriteSuperblockEntry
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
)
829 if ((fd
=open(DOSDrives
[drive
].device
,O_WRONLY
))==-1)
831 ERR("Cannot open the device %s (for writing)\n",
832 DOSDrives
[drive
].device
);
835 if (lseek(fd
,ofs
,SEEK_SET
)!=ofs
)
837 ERR("lseek failed on device %s !\n",
838 DOSDrives
[drive
].device
);
842 if (write(fd
,buff
,len
)!=len
)
845 ERR("Cannot write on %s !\n",
846 DOSDrives
[drive
].device
);
852 /******************************************************************
853 * static HANDLE CDROM_Open
857 static HANDLE
CDROM_Open(int drive
)
859 WCHAR root
[] = {'\\','\\','.','\\','A',':',0};
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;
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
))
887 if ((LOWORD(unicode_id
) == 0x2f25) /* Unicode ID */
888 && ((ver
== 0x40) || (ver
== 0x43) || (ver
== 0x45)))
889 { /* yippee, unicode */
892 for (i
=0; i
<LABEL_LEN
;i
++)
893 { /* Motorola -> Intel Unicode conversion :-\ */
895 label_read
[i
] = (ch
<< 8) | (ch
>> 8);
897 strncpyW(label
, label_read
, 11);
902 MultiByteToWideChar(DOSDrives
[drive
].codepage
, 0, (LPSTR
)label_read
, -1, label
, 11);
910 ERR("error reading label !\n");
914 /**************************************************************************
915 * CDROM_GetLabel [internal]
917 static DWORD
CDROM_GetLabel(int drive
, WCHAR
*label
)
924 h
= CDROM_Open(drive
);
927 r
= DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
,
928 0, &cdd
, sizeof(cdd
), &br
, 0);
933 switch (cdd
.DiskData
& 0x03)
935 case CDROM_DISK_DATA_TRACK
:
936 if (!CDROM_Data_GetLabel(drive
, label
))
939 case CDROM_DISK_AUDIO_TRACK
:
941 static const WCHAR audioCD
[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
942 strcpyW(label
, audioCD
);
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
))
957 TRACE("CD: label is %s\n", debugstr_w(label
));
961 /***********************************************************************
964 LPCWSTR
DRIVE_GetLabel( int drive
)
967 char buff
[DRIVE_SUPER
];
970 if (!DRIVE_IsValid( drive
)) return NULL
;
971 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
973 read
= CDROM_GetLabel(drive
, DOSDrives
[drive
].label_read
);
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'));
982 if (DOSDrives
[drive
].type
== DRIVE_REMOVABLE
||
983 DOSDrives
[drive
].type
== DRIVE_FIXED
)
986 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
988 MultiByteToWideChar(DOSDrives
[drive
].codepage
, 0, buff
+offs
, 11,
989 DOSDrives
[drive
].label_read
, 11);
990 DOSDrives
[drive
].label_read
[11]='\0';
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;
1012 DWORD dwStart
, dwEnd
, br
;
1015 if (!DeviceIoControl(h
, IOCTL_CDROM_READ_TOC
, NULL
, 0, &toc
, sizeof(toc
), &br
, 0))
1019 * wMagic collects the wFrames from track 1
1020 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
1022 * There it is collected for correcting the serial when there are less than
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
);
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
);
1051 BYTE b0
= 0, b1
= 1, b2
= 2, b3
= 3;
1054 if (dev
== -1) return 0;
1055 offs
= CDROM_Data_FindBestVoldesc(dev
);
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
];
1090 /**************************************************************************
1091 * CDROM_GetSerial [internal]
1093 static DWORD
CDROM_GetSerial(int drive
)
1097 CDROM_DISK_DATA cdd
;
1101 TRACE("%d\n", drive
);
1103 h
= CDROM_Open(drive
);
1106 r
= DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
,
1107 0, &cdd
, sizeof(cdd
), &br
, 0);
1114 switch (cdd
.DiskData
& 0x03)
1116 case CDROM_DISK_DATA_TRACK
:
1117 /* hopefully a data CD */
1118 serial
= CDROM_Data_GetSerial(drive
);
1120 case CDROM_DISK_AUDIO_TRACK
:
1122 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
1123 serial
= CDROM_Audio_GetSerial(h
);
1130 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial
), LOWORD(serial
));
1137 /***********************************************************************
1138 * DRIVE_GetSerialNumber
1140 DWORD
DRIVE_GetSerialNumber( int drive
)
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
:
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
);
1160 serial
= *((DWORD
*)(buff
+0x27));
1163 serial
= CDROM_GetSerial(drive
);
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;
1193 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return 0;
1194 DOSDrives
[drive
].serial_conf
= serial
;
1199 /***********************************************************************
1202 static UINT
DRIVE_GetType( int drive
)
1204 if (!DRIVE_IsValid( drive
)) return DRIVE_NO_ROOT_DIR
;
1205 return DOSDrives
[drive
].type
;
1209 /***********************************************************************
1212 UINT
DRIVE_GetFlags( int drive
)
1214 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
1215 return DOSDrives
[drive
].flags
;
1218 /***********************************************************************
1221 UINT
DRIVE_GetCodepage( int drive
)
1223 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
1224 return DOSDrives
[drive
].codepage
;
1228 /***********************************************************************
1231 int DRIVE_Chdir( int drive
, LPCWSTR path
)
1233 DOS_FULL_NAME full_name
;
1234 WCHAR buffer
[MAX_PATHNAME_LEN
];
1236 BY_HANDLE_FILE_INFORMATION info
;
1237 TDB
*pTask
= TASK_GetCurrent();
1239 buffer
[0] = 'A' + drive
;
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
);
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();
1276 /***********************************************************************
1279 int DRIVE_Disable( int drive
)
1281 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1283 SetLastError( ERROR_INVALID_DRIVE
);
1286 DOSDrives
[drive
].flags
|= DRIVE_DISABLED
;
1291 /***********************************************************************
1294 int DRIVE_Enable( int drive
)
1296 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1298 SetLastError( ERROR_INVALID_DRIVE
);
1301 DOSDrives
[drive
].flags
&= ~DRIVE_DISABLED
;
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
) ||
1321 (new_drive
< 0) || (new_drive
>= MAX_DOS_DRIVES
))
1323 SetLastError( ERROR_INVALID_DRIVE
);
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
))
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
);
1357 /***********************************************************************
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
)
1377 if (!DRIVE_IsValid(drive
))
1379 SetLastError( ERROR_PATH_NOT_FOUND
);
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)
1387 if (statfs( DOSDrives
[drive
].root
, &info
) < 0)
1391 WARN("cannot do statfs(%s)\n", DOSDrives
[drive
].root
);
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
);
1399 # ifdef HAVE_STRUCT_STATFS_F_BFREE
1400 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bfree
, info
.f_bsize
);
1402 # error "statfs has no bfree/bavail member!"
1405 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
1406 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1407 available
->QuadPart
= 0;
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
)
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
);
1435 /***********************************************************************
1438 * Build the environment array containing the drives' current directories.
1439 * Resulting pointer must be freed with HeapFree.
1441 char *DRIVE_BuildEnv(void)
1444 LPCWSTR cwd
[MAX_DOS_DRIVES
];
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
);
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
1498 * "E:\\TEST" "\\" TRUE
1499 * "E:\\TEST" ":\\" FALSE
1500 * "E:\\TEST" "E:\\" TRUE
1501 * "E:\\TEST" "" FALSE
1502 * "E:\\" "" FALSE (!)
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
;
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();
1523 if (root
[0] && root
[1] == ':') /* root contains drive tag */
1525 drive
= toupperW(root
[0]) - 'A';
1527 if (path
[0] == '\0')
1529 path
= DRIVE_GetDosCwd(drive
);
1532 SetLastError(ERROR_PATH_NOT_FOUND
);
1537 if (path
[0] == '\\')
1540 if (path
[0]) /* oops, we are in a subdir */
1542 SetLastError(ERROR_INVALID_NAME
);
1549 SetLastError(ERROR_PATH_NOT_FOUND
);
1551 SetLastError(ERROR_INVALID_NAME
);
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 */
1573 while (cluster_sec
* 65536 < size
.s
.LowPart
) cluster_sec
*= 2;
1575 if (cluster_sectors
)
1576 *cluster_sectors
= cluster_sec
;
1578 *sector_bytes
= sec_size
;
1580 *free_clusters
= available
.s
.LowPart
/ cluster_sec
;
1582 *total_clusters
= size
.s
.LowPart
/ cluster_sec
;
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
;
1599 if(!RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
1601 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1606 rootW
.Buffer
= NULL
;
1608 ret
= GetDiskFreeSpaceW(rootW
.Buffer
, cluster_sectors
, sector_bytes
,
1609 free_clusters
, total_clusters
);
1610 RtlFreeUnicodeString(&rootW
);
1616 /***********************************************************************
1617 * GetDiskFreeSpaceExW (KERNEL32.@)
1619 * This function is used to acquire the size of the available and
1620 * total space on a logical volume.
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
)
1634 ULARGE_INTEGER size
,available
;
1636 if (!root
) drive
= DRIVE_GetCurrentDrive();
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
));
1647 drive
= toupperW(root
[0]) - 'A';
1650 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1654 total
->s
.HighPart
= size
.s
.HighPart
;
1655 total
->s
.LowPart
= size
.s
.LowPart
;
1660 totalfree
->s
.HighPart
= available
.s
.HighPart
;
1661 totalfree
->s
.LowPart
= available
.s
.LowPart
;
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. */
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
;
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
;
1702 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
1703 else rootW
.Buffer
= NULL
;
1705 ret
= GetDiskFreeSpaceExW( rootW
.Buffer
, avail
, total
, totalfree
);
1707 RtlFreeUnicodeString(&rootW
);
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.
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
;
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.
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 */
1752 TRACE("(%s)\n", debugstr_w(root
));
1754 if (NULL
== root
) drive
= DRIVE_GetCurrentDrive();
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
;
1778 if( !RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
1780 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1785 rootW
.Buffer
= NULL
;
1787 ret
= GetDriveTypeW(rootW
.Buffer
);
1789 RtlFreeUnicodeString(&rootW
);
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
)
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
);
1821 GetLongPathNameW(shortname
, longname
, MAX_PATHNAME_LEN
);
1822 ret
= strlenW( longname
) + 1;
1823 if (ret
> buflen
) return ret
;
1824 strcpyW(buf
, longname
);
1828 /***********************************************************************
1829 * GetCurrentDirectoryA (KERNEL32.@)
1831 UINT WINAPI
GetCurrentDirectoryA( UINT buflen
, LPSTR buf
)
1833 WCHAR bufferW
[MAX_PATH
];
1836 retW
= GetCurrentDirectoryW(MAX_PATH
, bufferW
);
1840 else if (retW
> MAX_PATH
)
1842 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1847 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
1850 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buf
, buflen
, NULL
, NULL
);
1851 ret
--; /* length without 0 */
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();
1876 SetLastError(ERROR_INVALID_PARAMETER
);
1879 if (dir
[0] && (dir
[1]==':'))
1881 drive
= toupperW( *dir
) - 'A';
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
)))
1892 /* FIXME: what about empty strings? Add a \\ ? */
1893 if (!DRIVE_Chdir( drive
, dir
)) {
1894 DRIVE_SetCurrentDrive(olddrive
);
1901 /***********************************************************************
1902 * SetCurrentDirectoryA (KERNEL32.@)
1904 BOOL WINAPI
SetCurrentDirectoryA( LPCSTR dir
)
1906 UNICODE_STRING dirW
;
1911 SetLastError(ERROR_INVALID_PARAMETER
);
1915 if (RtlCreateUnicodeStringFromAsciiz(&dirW
, dir
))
1917 ret
= SetCurrentDirectoryW(dirW
.Buffer
);
1918 RtlFreeUnicodeString(&dirW
);
1921 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1926 /***********************************************************************
1927 * GetLogicalDriveStringsA (KERNEL32.@)
1929 UINT WINAPI
GetLogicalDriveStringsA( UINT len
, LPSTR buffer
)
1933 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1934 if (DRIVE_IsValid(drive
)) count
++;
1935 if ((count
* 4) + 1 <= len
)
1938 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1939 if (DRIVE_IsValid(drive
))
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
)
1962 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1963 if (DRIVE_IsValid(drive
)) count
++;
1964 if (count
* 4 * sizeof(WCHAR
) <= len
)
1967 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1968 if (DRIVE_IsValid(drive
))
1970 *p
++ = (WCHAR
)('a' + drive
);
1977 return count
* 4 * sizeof(WCHAR
);
1981 /***********************************************************************
1982 * GetLogicalDrives (KERNEL32.@)
1984 DWORD WINAPI
GetLogicalDrives(void)
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
);
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
)
2010 /* FIXME, SetLastError()s missing */
2012 if (!root
) drive
= DRIVE_GetCurrentDrive();
2015 if (root
[0] && root
[1] != ':')
2017 WARN("invalid root %s\n", debugstr_w(root
));
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
--;
2031 if (serial
) *serial
= DRIVE_GetSerialNumber(drive
);
2033 /* Set the filesystem information */
2034 /* Note: we only emulate a FAT fs at present */
2037 if (DOSDrives
[drive
].flags
& DRIVE_SHORT_NAMES
)
2040 *filename_len
= 255;
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
);
2060 static const WCHAR fatW
[] = {'F','A','T',0};
2061 strncpyW( fsname
, fatW
, fsname_len
);
2063 fsname
[fsname_len
- 1] = 0; /* ensure 0 termination */
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
;
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
);
2099 /***********************************************************************
2100 * SetVolumeLabelW (KERNEL32.@)
2102 BOOL WINAPI
SetVolumeLabelW( LPCWSTR root
, LPCWSTR volname
)
2106 /* FIXME, SetLastErrors missing */
2108 if (!root
) drive
= DRIVE_GetCurrentDrive();
2111 if ((root
[1]) && (root
[1] != ':'))
2113 WARN("invalid root %s\n", debugstr_w(root
));
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 */
2128 /***********************************************************************
2129 * SetVolumeLabelA (KERNEL32.@)
2131 BOOL WINAPI
SetVolumeLabelA(LPCSTR root
, LPCSTR volname
)
2133 UNICODE_STRING rootW
, volnameW
;
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
);
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
);