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"
35 #include <sys/types.h>
43 #ifdef HAVE_SYS_PARAM_H
44 # include <sys/param.h>
46 #ifdef STATFS_DEFINED_BY_SYS_VFS
49 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
50 # include <sys/mount.h>
52 # ifdef STATFS_DEFINED_BY_SYS_STATFS
53 # include <sys/statfs.h>
58 #define NONAMELESSUNION
59 #define NONAMELESSSTRUCT
65 #include "wine/winbase16.h" /* for GetCurrentTask */
74 #include "wine/unicode.h"
75 #include "wine/library.h"
76 #include "wine/server.h"
77 #include "wine/debug.h"
79 WINE_DEFAULT_DEBUG_CHANNEL(dosfs
);
80 WINE_DECLARE_DEBUG_CHANNEL(file
);
84 char *root
; /* root dir in Unix format without trailing / */
85 LPWSTR dos_cwd
; /* cwd in DOS format without leading or trailing \ */
86 char *unix_cwd
; /* cwd in Unix format without leading or trailing / */
87 char *device
; /* raw device path */
88 WCHAR label_conf
[12]; /* drive label as cfg'd in wine config */
89 WCHAR label_read
[12]; /* drive label as read from device */
90 DWORD serial_conf
; /* drive serial number as cfg'd in wine config */
91 UINT type
; /* drive type */
92 UINT flags
; /* drive flags */
93 UINT codepage
; /* drive code page */
94 dev_t dev
; /* unix device number */
95 ino_t ino
; /* unix inode number */
99 static const WCHAR DRIVE_Types
[][8] =
101 { 0 }, /* DRIVE_UNKNOWN */
102 { 0 }, /* DRIVE_NO_ROOT_DIR */
103 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
104 {'h','d',0}, /* DRIVE_FIXED */
105 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
106 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
107 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
111 /* Known filesystem types */
119 static const FS_DESCR DRIVE_Filesystems
[] =
121 { {'u','n','i','x',0}, DRIVE_CASE_SENSITIVE
| DRIVE_CASE_PRESERVING
},
122 { {'m','s','d','o','s',0}, DRIVE_SHORT_NAMES
},
123 { {'d','o','s',0}, DRIVE_SHORT_NAMES
},
124 { {'f','a','t',0}, DRIVE_SHORT_NAMES
},
125 { {'v','f','a','t',0}, DRIVE_CASE_PRESERVING
},
126 { {'w','i','n','9','5',0}, DRIVE_CASE_PRESERVING
},
131 static DOSDRIVE DOSDrives
[MAX_DOS_DRIVES
];
132 static int DRIVE_CurDrive
= -1;
134 static HTASK16 DRIVE_LastTask
= 0;
136 /* strdup on the process heap */
137 inline static char *heap_strdup( const char *str
)
139 INT len
= strlen(str
) + 1;
140 LPSTR p
= HeapAlloc( GetProcessHeap(), 0, len
);
141 if (p
) memcpy( p
, str
, len
);
145 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
147 extern void CDROM_InitRegistry(int dev
);
149 /***********************************************************************
152 static inline UINT
DRIVE_GetDriveType( INT drive
, LPCWSTR value
)
156 for (i
= 0; i
< sizeof(DRIVE_Types
)/sizeof(DRIVE_Types
[0]); i
++)
158 if (!strcmpiW( value
, DRIVE_Types
[i
] )) return i
;
160 MESSAGE("Drive %c: unknown drive type %s, defaulting to 'hd'.\n",
161 'A' + drive
, debugstr_w(value
) );
166 /***********************************************************************
169 static UINT
DRIVE_GetFSFlags( INT drive
, 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("Drive %c: unknown filesystem type %s, defaulting to 'win95'.\n",
176 'A' + drive
, debugstr_w(value
) );
177 return DRIVE_CASE_PRESERVING
;
181 /***********************************************************************
186 int i
, len
, count
= 0;
187 WCHAR driveW
[] = {'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
188 'W','i','n','e','\\','W','i','n','e','\\',
189 'C','o','n','f','i','g','\\','D','r','i','v','e',' ','A',0};
190 WCHAR drive_env
[] = {'=','A',':',0};
191 WCHAR path
[MAX_PATHNAME_LEN
];
192 char tmp
[MAX_PATHNAME_LEN
*sizeof(WCHAR
) + sizeof(KEY_VALUE_PARTIAL_INFORMATION
)];
193 struct stat drive_stat_buffer
;
198 OBJECT_ATTRIBUTES attr
;
199 UNICODE_STRING nameW
;
201 static const WCHAR PathW
[] = {'P','a','t','h',0};
202 static const WCHAR CodepageW
[] = {'C','o','d','e','p','a','g','e',0};
203 static const WCHAR LabelW
[] = {'L','a','b','e','l',0};
204 static const WCHAR SerialW
[] = {'S','e','r','i','a','l',0};
205 static const WCHAR TypeW
[] = {'T','y','p','e',0};
206 static const WCHAR FilesystemW
[] = {'F','i','l','e','s','y','s','t','e','m',0};
207 static const WCHAR DeviceW
[] = {'D','e','v','i','c','e',0};
208 static const WCHAR ReadVolInfoW
[] = {'R','e','a','d','V','o','l','I','n','f','o',0};
209 static const WCHAR FailReadOnlyW
[] = {'F','a','i','l','R','e','a','d','O','n','l','y',0};
210 static const WCHAR driveC_labelW
[] = {'D','r','i','v','e',' ','C',' ',' ',' ',' ',0};
213 attr
.Length
= sizeof(attr
);
214 attr
.RootDirectory
= 0;
215 attr
.ObjectName
= &nameW
;
217 attr
.SecurityDescriptor
= NULL
;
218 attr
.SecurityQualityOfService
= NULL
;
220 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
222 RtlInitUnicodeString( &nameW
, driveW
);
223 nameW
.Buffer
[(nameW
.Length
/ sizeof(WCHAR
)) - 1] = 'A' + i
;
224 if (NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
) != STATUS_SUCCESS
) continue;
226 /* Get the code page number */
227 RtlInitUnicodeString( &nameW
, CodepageW
);
228 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
230 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
231 drive
->codepage
= strtolW( data
, NULL
, 10 );
234 /* Get the root path */
235 RtlInitUnicodeString( &nameW
, PathW
);
236 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
238 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
239 ExpandEnvironmentStringsW( data
, path
, sizeof(path
)/sizeof(WCHAR
) );
241 p
= path
+ strlenW(path
) - 1;
242 while ((p
> path
) && (*p
== '/')) *p
-- = '\0';
246 len
= WideCharToMultiByte(drive
->codepage
, 0, path
, -1, NULL
, 0, NULL
, NULL
);
247 drive
->root
= HeapAlloc(GetProcessHeap(), 0, len
);
248 WideCharToMultiByte(drive
->codepage
, 0, path
, -1, drive
->root
, len
, NULL
, NULL
);
252 /* relative paths are relative to config dir */
253 const char *config
= wine_get_config_dir();
254 len
= strlen(config
);
255 len
+= WideCharToMultiByte(drive
->codepage
, 0, path
, -1, NULL
, 0, NULL
, NULL
) + 2;
256 drive
->root
= HeapAlloc( GetProcessHeap(), 0, len
);
257 len
-= sprintf( drive
->root
, "%s/", config
);
258 WideCharToMultiByte(drive
->codepage
, 0, path
, -1,
259 drive
->root
+ strlen(drive
->root
), len
, NULL
, NULL
);
262 if (stat( drive
->root
, &drive_stat_buffer
))
264 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
265 drive
->root
, strerror(errno
), 'A' + i
);
266 HeapFree( GetProcessHeap(), 0, drive
->root
);
270 if (!S_ISDIR(drive_stat_buffer
.st_mode
))
272 MESSAGE("%s is not a directory, ignoring drive %c:\n",
273 drive
->root
, 'A' + i
);
274 HeapFree( GetProcessHeap(), 0, drive
->root
);
279 drive
->dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(drive
->dos_cwd
[0]));
280 drive
->unix_cwd
= heap_strdup( "" );
281 drive
->device
= NULL
;
283 drive
->dev
= drive_stat_buffer
.st_dev
;
284 drive
->ino
= drive_stat_buffer
.st_ino
;
286 /* Get the drive type */
287 RtlInitUnicodeString( &nameW
, TypeW
);
288 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
290 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
291 drive
->type
= DRIVE_GetDriveType( i
, data
);
293 else drive
->type
= DRIVE_FIXED
;
295 /* Get the drive label */
296 RtlInitUnicodeString( &nameW
, LabelW
);
297 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
299 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
300 lstrcpynW( drive
->label_conf
, data
, 12 );
302 if ((len
= strlenW(drive
->label_conf
)) < 11)
304 /* Pad label with spaces */
305 while(len
< 11) drive
->label_conf
[len
++] = ' ';
306 drive
->label_conf
[11] = '\0';
309 /* Get the serial number */
310 RtlInitUnicodeString( &nameW
, SerialW
);
311 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
313 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
314 drive
->serial_conf
= strtoulW( data
, NULL
, 16 );
316 else drive
->serial_conf
= 12345678;
318 /* Get the filesystem type */
319 RtlInitUnicodeString( &nameW
, FilesystemW
);
320 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
322 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
323 drive
->flags
= DRIVE_GetFSFlags( i
, data
);
325 else drive
->flags
= DRIVE_CASE_PRESERVING
;
328 RtlInitUnicodeString( &nameW
, DeviceW
);
329 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
331 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
332 len
= WideCharToMultiByte(CP_ACP
, 0, data
, -1, NULL
, 0, NULL
, NULL
);
333 drive
->device
= HeapAlloc(GetProcessHeap(), 0, len
);
334 WideCharToMultiByte(drive
->codepage
, 0, data
, -1, drive
->device
, len
, NULL
, NULL
);
336 RtlInitUnicodeString( &nameW
, ReadVolInfoW
);
337 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
339 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
340 if (IS_OPTION_TRUE(data
[0])) drive
->flags
|= DRIVE_READ_VOL_INFO
;
342 else drive
->flags
|= DRIVE_READ_VOL_INFO
;
344 if (drive
->type
== DRIVE_CDROM
)
347 if ((cd_fd
= open(drive
->device
, O_RDONLY
|O_NONBLOCK
)) != -1)
349 CDROM_InitRegistry(cd_fd
);
355 /* Get the FailReadOnly flag */
356 RtlInitUnicodeString( &nameW
, FailReadOnlyW
);
357 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
359 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
360 if (IS_OPTION_TRUE(data
[0])) drive
->flags
|= DRIVE_FAIL_READ_ONLY
;
363 /* Make the first hard disk the current drive */
364 if ((DRIVE_CurDrive
== -1) && (drive
->type
== DRIVE_FIXED
))
368 TRACE("Drive %c: path=%s type=%s label=%s serial=%08lx "
369 "flags=%08x codepage=%u dev=%x ino=%x\n",
370 'A' + i
, drive
->root
, debugstr_w(DRIVE_Types
[drive
->type
]),
371 debugstr_w(drive
->label_conf
), drive
->serial_conf
, drive
->flags
,
372 drive
->codepage
, (int)drive
->dev
, (int)drive
->ino
);
381 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
382 /* Create a C drive pointing to Unix root dir */
383 DOSDrives
[2].root
= heap_strdup( "/" );
384 DOSDrives
[2].dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(DOSDrives
[2].dos_cwd
[0]));
385 DOSDrives
[2].unix_cwd
= heap_strdup( "" );
386 strcpyW( DOSDrives
[2].label_conf
, driveC_labelW
);
387 DOSDrives
[2].serial_conf
= 12345678;
388 DOSDrives
[2].type
= DRIVE_FIXED
;
389 DOSDrives
[2].device
= NULL
;
390 DOSDrives
[2].flags
= 0;
394 /* Make sure the current drive is valid */
395 if (DRIVE_CurDrive
== -1)
397 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
399 if (drive
->root
&& !(drive
->flags
& DRIVE_DISABLED
))
407 /* get current working directory info for all drives */
408 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++, drive_env
[1]++)
410 if (!GetEnvironmentVariableW(drive_env
, path
, MAX_PATHNAME_LEN
)) continue;
412 if (toupperW(path
[0]) != drive_env
[1] || path
[1] != ':') continue;
413 DRIVE_Chdir( i
, path
+ 2 );
419 /***********************************************************************
422 int DRIVE_IsValid( int drive
)
424 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
425 return (DOSDrives
[drive
].root
&&
426 !(DOSDrives
[drive
].flags
& DRIVE_DISABLED
));
430 /***********************************************************************
431 * DRIVE_GetCurrentDrive
433 int DRIVE_GetCurrentDrive(void)
435 TDB
*pTask
= GlobalLock16(GetCurrentTask());
436 if (pTask
&& (pTask
->curdrive
& 0x80)) return pTask
->curdrive
& ~0x80;
437 return DRIVE_CurDrive
;
441 /***********************************************************************
442 * DRIVE_SetCurrentDrive
444 int DRIVE_SetCurrentDrive( int drive
)
446 TDB
*pTask
= GlobalLock16(GetCurrentTask());
447 if (!DRIVE_IsValid( drive
))
449 SetLastError( ERROR_INVALID_DRIVE
);
452 TRACE("%c:\n", 'A' + drive
);
453 DRIVE_CurDrive
= drive
;
454 if (pTask
) pTask
->curdrive
= drive
| 0x80;
459 /***********************************************************************
460 * DRIVE_FindDriveRoot
462 * Find a drive for which the root matches the beginning of the given path.
463 * This can be used to translate a Unix path into a drive + DOS path.
464 * Return value is the drive, or -1 on error. On success, path is modified
465 * to point to the beginning of the DOS path.
467 * Note: path must be in the encoding of the underlying Unix file system.
469 int DRIVE_FindDriveRoot( const char **path
)
471 /* Starting with the full path, check if the device and inode match any of
472 * the wine 'drives'. If not then remove the last path component and try
473 * again. If the last component was a '..' then skip a normal component
474 * since it's a directory that's ascended back out of.
476 int drive
, level
, len
;
477 char buffer
[MAX_PATHNAME_LEN
];
481 strcpy( buffer
, *path
);
482 while ((p
= strchr( buffer
, '\\' )) != NULL
)
484 len
= strlen(buffer
);
486 /* strip off trailing slashes */
487 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
492 if (stat( buffer
, &st
) == 0 && S_ISDIR( st
.st_mode
))
494 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
496 if (!DOSDrives
[drive
].root
||
497 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
))
500 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
501 (DOSDrives
[drive
].ino
== st
.st_ino
))
503 if (len
== 1) len
= 0; /* preserve root slash in returned path */
504 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
505 *path
, 'A' + drive
, buffer
, *path
+ len
);
507 if (!**path
) *path
= "\\";
512 if (len
<= 1) return -1; /* reached root */
517 /* find start of the last path component */
518 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
519 if (!buffer
[len
]) break; /* empty component -> reached root */
520 /* does removing it take us up a level? */
521 if (strcmp( buffer
+ len
, "." ) != 0)
522 level
+= strcmp( buffer
+ len
, ".." ) ? 1 : -1;
524 /* strip off trailing slashes */
525 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
531 /***********************************************************************
532 * DRIVE_FindDriveRootW
534 * Unicode version of DRIVE_FindDriveRoot.
536 int DRIVE_FindDriveRootW( LPCWSTR
*path
)
538 int drive
, level
, len
;
539 WCHAR buffer
[MAX_PATHNAME_LEN
];
543 strcpyW( buffer
, *path
);
544 while ((p
= strchrW( buffer
, '\\' )) != NULL
)
546 len
= strlenW(buffer
);
548 /* strip off trailing slashes */
549 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
556 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
558 char buffA
[MAX_PATHNAME_LEN
];
560 if (!DOSDrives
[drive
].root
||
561 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
))
564 if (codepage
!= DOSDrives
[drive
].codepage
)
566 WideCharToMultiByte( DOSDrives
[drive
].codepage
, 0, buffer
, -1,
567 buffA
, sizeof(buffA
), NULL
, NULL
);
568 if (stat( buffA
, &st
) == -1 || !S_ISDIR( st
.st_mode
))
573 codepage
= DOSDrives
[drive
].codepage
;
576 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
577 (DOSDrives
[drive
].ino
== st
.st_ino
))
579 static const WCHAR rootW
[] = {'\\',0};
581 if (len
== 1) len
= 0; /* preserve root slash in returned path */
582 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
583 debugstr_w(*path
), 'A' + drive
, debugstr_w(buffer
), debugstr_w(*path
+ len
));
585 if (!**path
) *path
= rootW
;
589 if (len
<= 1) return -1; /* reached root */
594 static const WCHAR dotW
[] = {'.',0};
595 static const WCHAR dotdotW
[] = {'.','.',0};
597 /* find start of the last path component */
598 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
599 if (!buffer
[len
]) break; /* empty component -> reached root */
600 /* does removing it take us up a level? */
601 if (strcmpW( buffer
+ len
, dotW
) != 0)
602 level
+= strcmpW( buffer
+ len
, dotdotW
) ? 1 : -1;
604 /* strip off trailing slashes */
605 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
611 /***********************************************************************
614 const char * DRIVE_GetRoot( int drive
)
616 if (!DRIVE_IsValid( drive
)) return NULL
;
617 return DOSDrives
[drive
].root
;
621 /***********************************************************************
624 LPCWSTR
DRIVE_GetDosCwd( int drive
)
626 TDB
*pTask
= GlobalLock16(GetCurrentTask());
627 if (!DRIVE_IsValid( drive
)) return NULL
;
629 /* Check if we need to change the directory to the new task. */
630 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
631 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
632 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
634 static const WCHAR rootW
[] = {'\\',0};
635 WCHAR curdirW
[MAX_PATH
];
636 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
637 /* Perform the task-switch */
638 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
639 DRIVE_LastTask
= GetCurrentTask();
641 return DOSDrives
[drive
].dos_cwd
;
645 /***********************************************************************
648 const char * DRIVE_GetUnixCwd( int drive
)
650 TDB
*pTask
= GlobalLock16(GetCurrentTask());
651 if (!DRIVE_IsValid( drive
)) return NULL
;
653 /* Check if we need to change the directory to the new task. */
654 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
655 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
656 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
658 static const WCHAR rootW
[] = {'\\',0};
659 WCHAR curdirW
[MAX_PATH
];
660 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
661 /* Perform the task-switch */
662 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
663 DRIVE_LastTask
= GetCurrentTask();
665 return DOSDrives
[drive
].unix_cwd
;
669 /***********************************************************************
672 const char * DRIVE_GetDevice( int drive
)
674 return (DRIVE_IsValid( drive
)) ? DOSDrives
[drive
].device
: NULL
;
677 /******************************************************************
678 * static WORD CDROM_Data_FindBestVoldesc
682 static WORD
CDROM_Data_FindBestVoldesc(int fd
)
684 BYTE cur_vd_type
, max_vd_type
= 0;
685 unsigned int offs
, best_offs
= 0, extra_offs
= 0;
688 for (offs
= 0x8000; offs
<= 0x9800; offs
+= 0x800)
690 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
691 * the volume label is displaced forward by 8
693 lseek(fd
, offs
+ 11, SEEK_SET
); /* check for non-ISO9660 signature */
695 if ((sig
[0] == 'R') && (sig
[1] == 'O') && (sig
[2]=='M'))
699 lseek(fd
, offs
+ extra_offs
, SEEK_SET
);
700 read(fd
, &cur_vd_type
, 1);
701 if (cur_vd_type
== 0xff) /* voldesc set terminator */
703 if (cur_vd_type
> max_vd_type
)
705 max_vd_type
= cur_vd_type
;
706 best_offs
= offs
+ extra_offs
;
712 /***********************************************************************
713 * DRIVE_ReadSuperblock
716 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
717 * to check, that they are writing on a FAT filesystem !
719 int DRIVE_ReadSuperblock (int drive
, char * buff
)
721 #define DRIVE_SUPER 96
725 struct stat stat_buf
;
727 memset(buff
, 0, DRIVE_SUPER
);
728 /* O_NONBLOCK in case we're opening FIFO; will be reset later */
729 if ((fd
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NOCTTY
|O_NONBLOCK
)) != -1) {
730 if (fstat(fd
, &stat_buf
) < 0) { /* shouldn't happen since we just opened that file */
731 ERR("fstat() failed for opened device '%s' (drive %c:) ! IT SHOULDN'T HAPPEN !!!\n",
732 DOSDrives
[drive
].device
, 'A'+drive
);
734 } else if (!S_ISBLK(stat_buf
.st_mode
)) {
735 ERR("Device '%s' (drive %c:) is not a block device - check your config\n",
736 DOSDrives
[drive
].device
, 'A'+drive
);
738 /* reset O_NONBLOCK */
739 } else if (fcntl(fd
, F_SETFL
, 0) < 0 || fcntl(fd
, F_GETFL
) & O_NONBLOCK
) {
740 ERR("fcntl() failed to reset O_NONBLOCK for device '%s' (drive %c:)\n",
741 DOSDrives
[drive
].device
, 'A'+drive
);
749 if (!DOSDrives
[drive
].device
)
750 ERR("No device configured for drive %c: !\n", 'A'+drive
);
752 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives
[drive
].device
, 'A'+drive
,
753 (stat(DOSDrives
[drive
].device
, &stat_buf
)) ?
754 "not available or symlink not valid ?" : "no permission");
757 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
761 switch(DOSDrives
[drive
].type
)
763 case DRIVE_REMOVABLE
:
768 offs
= CDROM_Data_FindBestVoldesc(fd
);
775 if ((offs
) && (lseek(fd
,offs
,SEEK_SET
)!=offs
))
780 if (read(fd
,buff
,DRIVE_SUPER
)!=DRIVE_SUPER
)
786 switch(DOSDrives
[drive
].type
)
788 case DRIVE_REMOVABLE
:
790 if ((buff
[0x26]!=0x29) || /* Check for FAT present */
791 /* FIXME: do really all FAT have their name beginning with
792 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
793 memcmp( buff
+0x36,"FAT",3))
795 ERR("The filesystem is not FAT !! (device=%s)\n",
796 DOSDrives
[drive
].device
);
802 if (strncmp(&buff
[1],"CD001",5)) /* Check for iso9660 present */
807 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
821 /***********************************************************************
822 * DRIVE_WriteSuperblockEntry
825 * We are writing as little as possible (ie. not the whole SuperBlock)
826 * not to interfere with kernel. The drive can be mounted !
828 int DRIVE_WriteSuperblockEntry (int drive
, off_t ofs
, size_t len
, char * buff
)
832 if ((fd
=open(DOSDrives
[drive
].device
,O_WRONLY
))==-1)
834 ERR("Cannot open the device %s (for writing)\n",
835 DOSDrives
[drive
].device
);
838 if (lseek(fd
,ofs
,SEEK_SET
)!=ofs
)
840 ERR("lseek failed on device %s !\n",
841 DOSDrives
[drive
].device
);
845 if (write(fd
,buff
,len
)!=len
)
848 ERR("Cannot write on %s !\n",
849 DOSDrives
[drive
].device
);
855 /******************************************************************
856 * static HANDLE CDROM_Open
860 static HANDLE
CDROM_Open(int drive
)
862 WCHAR root
[] = {'\\','\\','.','\\','A',':',0};
864 return CreateFileW(root
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0);
867 /**************************************************************************
868 * CDROM_Data_GetLabel [internal]
870 DWORD
CDROM_Data_GetLabel(int drive
, WCHAR
*label
)
872 #define LABEL_LEN 32+1
873 int dev
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NONBLOCK
);
874 WORD offs
= CDROM_Data_FindBestVoldesc(dev
);
875 WCHAR label_read
[LABEL_LEN
]; /* Unicode possible, too */
876 DWORD unicode_id
= 0;
880 if ((lseek(dev
, offs
+0x58, SEEK_SET
) == offs
+0x58)
881 && (read(dev
, &unicode_id
, 3) == 3))
883 int ver
= (unicode_id
& 0xff0000) >> 16;
885 if ((lseek(dev
, offs
+0x28, SEEK_SET
) != offs
+0x28)
886 || (read(dev
, &label_read
, LABEL_LEN
) != LABEL_LEN
))
890 if ((LOWORD(unicode_id
) == 0x2f25) /* Unicode ID */
891 && ((ver
== 0x40) || (ver
== 0x43) || (ver
== 0x45)))
892 { /* yippee, unicode */
895 for (i
=0; i
<LABEL_LEN
;i
++)
896 { /* Motorola -> Intel Unicode conversion :-\ */
898 label_read
[i
] = (ch
<< 8) | (ch
>> 8);
900 strncpyW(label
, label_read
, 11);
905 MultiByteToWideChar(DOSDrives
[drive
].codepage
, 0, (LPSTR
)label_read
, -1, label
, 11);
913 ERR("error reading label !\n");
917 /**************************************************************************
918 * CDROM_GetLabel [internal]
920 static DWORD
CDROM_GetLabel(int drive
, WCHAR
*label
)
927 h
= CDROM_Open(drive
);
930 r
= DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
,
931 0, &cdd
, sizeof(cdd
), &br
, 0);
936 switch (cdd
.DiskData
& 0x03)
938 case CDROM_DISK_DATA_TRACK
:
939 if (!CDROM_Data_GetLabel(drive
, label
))
942 case CDROM_DISK_AUDIO_TRACK
:
944 static const WCHAR audioCD
[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
945 strcpyW(label
, audioCD
);
948 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
949 FIXME("Need to get the label of a mixed mode CD!\n");
950 /* This assumes that the first track is a data track! */
951 /* I guess the correct way would be to enumerate all data tracks
952 and check each for the title */
953 if (!CDROM_Data_GetLabel(drive
, label
))
960 TRACE("CD: label is %s\n", debugstr_w(label
));
964 /***********************************************************************
967 LPCWSTR
DRIVE_GetLabel( int drive
)
970 char buff
[DRIVE_SUPER
];
973 if (!DRIVE_IsValid( drive
)) return NULL
;
974 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
976 read
= CDROM_GetLabel(drive
, DOSDrives
[drive
].label_read
);
979 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
981 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
982 ERR("Invalid or unreadable superblock on %s (%c:).\n",
983 DOSDrives
[drive
].device
, (char)(drive
+'A'));
985 if (DOSDrives
[drive
].type
== DRIVE_REMOVABLE
||
986 DOSDrives
[drive
].type
== DRIVE_FIXED
)
989 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
991 MultiByteToWideChar(DOSDrives
[drive
].codepage
, 0, buff
+offs
, 11,
992 DOSDrives
[drive
].label_read
, 11);
993 DOSDrives
[drive
].label_read
[11]='\0';
999 DOSDrives
[drive
].label_read
: DOSDrives
[drive
].label_conf
;
1002 #define CDFRAMES_PERSEC 75
1003 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
1004 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
1005 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
1007 /**************************************************************************
1008 * CDROM_Audio_GetSerial [internal]
1010 static DWORD
CDROM_Audio_GetSerial(HANDLE h
)
1012 unsigned long serial
= 0;
1015 DWORD dwStart
, dwEnd
, br
;
1018 if (!DeviceIoControl(h
, IOCTL_CDROM_READ_TOC
, NULL
, 0, &toc
, sizeof(toc
), &br
, 0))
1022 * wMagic collects the wFrames from track 1
1023 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
1025 * There it is collected for correcting the serial when there are less than
1028 wMagic
= toc
.TrackData
[0].Address
[2];
1029 dwStart
= FRAME_OF_TOC(toc
, toc
.FirstTrack
);
1031 for (i
= 0; i
<= toc
.LastTrack
- toc
.FirstTrack
; i
++) {
1032 serial
+= (toc
.TrackData
[i
].Address
[0] << 16) |
1033 (toc
.TrackData
[i
].Address
[1] << 8) | toc
.TrackData
[i
].Address
[2];
1035 dwEnd
= FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1);
1037 if (toc
.LastTrack
- toc
.FirstTrack
+ 1 < 3)
1038 serial
+= wMagic
+ (dwEnd
- dwStart
);
1043 /**************************************************************************
1044 * CDROM_Data_GetSerial [internal]
1046 static DWORD
CDROM_Data_GetSerial(int drive
)
1048 int dev
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NONBLOCK
);
1054 BYTE b0
= 0, b1
= 1, b2
= 2, b3
= 3;
1057 if (dev
== -1) return 0;
1058 offs
= CDROM_Data_FindBestVoldesc(dev
);
1067 lseek(dev
, offs
, SEEK_SET
);
1068 read(dev
, buf
, 2048);
1070 * OK, another braindead one... argh. Just believe it.
1071 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
1072 * It's true and nobody will ever be able to change it.
1074 ovi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
1075 GetVersionExA(&ovi
);
1076 if ((ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) && (ovi
.dwMajorVersion
>= 4))
1078 b0
= 3; b1
= 2; b2
= 1; b3
= 0;
1080 for (i
= 0; i
< 2048; i
+= 4)
1082 /* DON'T optimize this into DWORD !! (breaks overflow) */
1083 serial
.p
[b0
] += buf
[i
+b0
];
1084 serial
.p
[b1
] += buf
[i
+b1
];
1085 serial
.p
[b2
] += buf
[i
+b2
];
1086 serial
.p
[b3
] += buf
[i
+b3
];
1093 /**************************************************************************
1094 * CDROM_GetSerial [internal]
1096 static DWORD
CDROM_GetSerial(int drive
)
1100 CDROM_DISK_DATA cdd
;
1104 TRACE("%d\n", drive
);
1106 h
= CDROM_Open(drive
);
1109 r
= DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
,
1110 0, &cdd
, sizeof(cdd
), &br
, 0);
1117 switch (cdd
.DiskData
& 0x03)
1119 case CDROM_DISK_DATA_TRACK
:
1120 /* hopefully a data CD */
1121 serial
= CDROM_Data_GetSerial(drive
);
1123 case CDROM_DISK_AUDIO_TRACK
:
1125 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
1126 serial
= CDROM_Audio_GetSerial(h
);
1133 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial
), LOWORD(serial
));
1140 /***********************************************************************
1141 * DRIVE_GetSerialNumber
1143 DWORD
DRIVE_GetSerialNumber( int drive
)
1146 char buff
[DRIVE_SUPER
];
1148 TRACE("drive %d, type = %d\n", drive
, DOSDrives
[drive
].type
);
1150 if (!DRIVE_IsValid( drive
)) return 0;
1152 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
1154 switch(DOSDrives
[drive
].type
)
1156 case DRIVE_REMOVABLE
:
1158 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
1159 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
1160 " Maybe not FAT?\n" ,
1161 DOSDrives
[drive
].device
, 'A'+drive
);
1163 serial
= *((DWORD
*)(buff
+0x27));
1166 serial
= CDROM_GetSerial(drive
);
1169 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive
+'A');
1173 return (serial
) ? serial
: DOSDrives
[drive
].serial_conf
;
1177 /***********************************************************************
1178 * DRIVE_SetSerialNumber
1180 int DRIVE_SetSerialNumber( int drive
, DWORD serial
)
1182 char buff
[DRIVE_SUPER
];
1184 if (!DRIVE_IsValid( drive
)) return 0;
1186 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
1188 if ((DOSDrives
[drive
].type
!= DRIVE_REMOVABLE
) &&
1189 (DOSDrives
[drive
].type
!= DRIVE_FIXED
)) return 0;
1190 /* check, if the drive has a FAT filesystem */
1191 if (DRIVE_ReadSuperblock(drive
, buff
)) return 0;
1192 if (DRIVE_WriteSuperblockEntry(drive
, 0x27, 4, (char *) &serial
)) return 0;
1196 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return 0;
1197 DOSDrives
[drive
].serial_conf
= serial
;
1202 /***********************************************************************
1205 static UINT
DRIVE_GetType( int drive
)
1207 if (!DRIVE_IsValid( drive
)) return DRIVE_NO_ROOT_DIR
;
1208 return DOSDrives
[drive
].type
;
1212 /***********************************************************************
1215 UINT
DRIVE_GetFlags( int drive
)
1217 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
1218 return DOSDrives
[drive
].flags
;
1221 /***********************************************************************
1224 UINT
DRIVE_GetCodepage( int drive
)
1226 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
1227 return DOSDrives
[drive
].codepage
;
1231 /***********************************************************************
1234 int DRIVE_Chdir( int drive
, LPCWSTR path
)
1236 DOS_FULL_NAME full_name
;
1237 WCHAR buffer
[MAX_PATHNAME_LEN
];
1239 BY_HANDLE_FILE_INFORMATION info
;
1240 TDB
*pTask
= GlobalLock16(GetCurrentTask());
1242 buffer
[0] = 'A' + drive
;
1245 TRACE("(%s,%s)\n", debugstr_w(buffer
), debugstr_w(path
) );
1246 strncpyW( buffer
+ 2, path
, MAX_PATHNAME_LEN
- 2 );
1247 buffer
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1249 if (!DOSFS_GetFullName( buffer
, TRUE
, &full_name
)) return 0;
1250 if (!FILE_Stat( full_name
.long_name
, &info
, NULL
)) return 0;
1251 if (!(info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1253 SetLastError( ERROR_FILE_NOT_FOUND
);
1256 unix_cwd
= full_name
.long_name
+ strlen( DOSDrives
[drive
].root
);
1257 while (*unix_cwd
== '/') unix_cwd
++;
1259 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1260 'A' + drive
, unix_cwd
, debugstr_w(full_name
.short_name
+ 3) );
1262 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].dos_cwd
);
1263 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].unix_cwd
);
1264 DOSDrives
[drive
].dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name
.short_name
) - 2) * sizeof(WCHAR
));
1265 strcpyW(DOSDrives
[drive
].dos_cwd
, full_name
.short_name
+ 3);
1266 DOSDrives
[drive
].unix_cwd
= heap_strdup( unix_cwd
);
1268 if (pTask
&& (pTask
->curdrive
& 0x80) &&
1269 ((pTask
->curdrive
& ~0x80) == drive
))
1271 WideCharToMultiByte(CP_ACP
, 0, full_name
.short_name
+ 2, -1,
1272 pTask
->curdir
, sizeof(pTask
->curdir
), NULL
, NULL
);
1273 DRIVE_LastTask
= GetCurrentTask();
1279 /***********************************************************************
1282 int DRIVE_Disable( int drive
)
1284 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1286 SetLastError( ERROR_INVALID_DRIVE
);
1289 DOSDrives
[drive
].flags
|= DRIVE_DISABLED
;
1294 /***********************************************************************
1297 int DRIVE_Enable( int drive
)
1299 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1301 SetLastError( ERROR_INVALID_DRIVE
);
1304 DOSDrives
[drive
].flags
&= ~DRIVE_DISABLED
;
1309 /***********************************************************************
1310 * DRIVE_SetLogicalMapping
1312 int DRIVE_SetLogicalMapping ( int existing_drive
, int new_drive
)
1314 /* If new_drive is already valid, do nothing and return 0
1315 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1317 DOSDRIVE
*old
, *new;
1319 old
= DOSDrives
+ existing_drive
;
1320 new = DOSDrives
+ new_drive
;
1322 if ((existing_drive
< 0) || (existing_drive
>= MAX_DOS_DRIVES
) ||
1324 (new_drive
< 0) || (new_drive
>= MAX_DOS_DRIVES
))
1326 SetLastError( ERROR_INVALID_DRIVE
);
1332 TRACE("Can't map drive %c: to already existing drive %c:\n",
1333 'A' + existing_drive
, 'A' + new_drive
);
1334 /* it is already mapped there, so return success */
1335 if (!strcmp(old
->root
,new->root
))
1340 new->root
= heap_strdup( old
->root
);
1341 new->dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(old
->dos_cwd
) + 1) * sizeof(WCHAR
));
1342 strcpyW(new->dos_cwd
, old
->dos_cwd
);
1343 new->unix_cwd
= heap_strdup( old
->unix_cwd
);
1344 new->device
= heap_strdup( old
->device
);
1345 memcpy ( new->label_conf
, old
->label_conf
, 12 );
1346 memcpy ( new->label_read
, old
->label_read
, 12 );
1347 new->serial_conf
= old
->serial_conf
;
1348 new->type
= old
->type
;
1349 new->flags
= old
->flags
;
1350 new->dev
= old
->dev
;
1351 new->ino
= old
->ino
;
1353 TRACE("Drive %c: is now equal to drive %c:\n",
1354 'A' + new_drive
, 'A' + existing_drive
);
1360 /***********************************************************************
1363 * Open the drive raw device and return a Unix fd (or -1 on error).
1365 int DRIVE_OpenDevice( int drive
, int flags
)
1367 if (!DRIVE_IsValid( drive
)) return -1;
1368 return open( DOSDrives
[drive
].device
, flags
);
1372 /***********************************************************************
1373 * DRIVE_GetFreeSpace
1375 static int DRIVE_GetFreeSpace( int drive
, PULARGE_INTEGER size
,
1376 PULARGE_INTEGER available
)
1380 if (!DRIVE_IsValid(drive
))
1382 SetLastError( ERROR_PATH_NOT_FOUND
);
1386 /* FIXME: add autoconf check for this */
1387 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1388 if (statfs( DOSDrives
[drive
].root
, &info
, 0, 0) < 0)
1390 if (statfs( DOSDrives
[drive
].root
, &info
) < 0)
1394 WARN("cannot do statfs(%s)\n", DOSDrives
[drive
].root
);
1398 size
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bsize
, info
.f_blocks
);
1399 #ifdef HAVE_STRUCT_STATFS_F_BAVAIL
1400 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bavail
, info
.f_bsize
);
1402 # ifdef HAVE_STRUCT_STATFS_F_BFREE
1403 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bfree
, info
.f_bsize
);
1405 # error "statfs has no bfree/bavail member!"
1408 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
1409 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1410 available
->QuadPart
= 0;
1415 /***********************************************************************
1416 * DRIVE_GetCurrentDirectory
1417 * Returns "X:\\path\\etc\\".
1419 * Despite the API description, return required length including the
1420 * terminating null when buffer too small. This is the real behaviour.
1422 static UINT
DRIVE_GetCurrentDirectory( UINT buflen
, LPWSTR buf
)
1425 LPCWSTR dos_cwd
= DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1426 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
1428 ret
= strlenW(dos_cwd
) + 3; /* length of WHOLE current directory */
1429 if (ret
>= buflen
) return ret
+ 1;
1431 strcpyW( buf
, driveA_rootW
);
1432 buf
[0] += DRIVE_GetCurrentDrive();
1433 strcatW( buf
, dos_cwd
);
1438 /***********************************************************************
1441 * Build the environment array containing the drives' current directories.
1442 * Resulting pointer must be freed with HeapFree.
1444 char *DRIVE_BuildEnv(void)
1447 LPCWSTR cwd
[MAX_DOS_DRIVES
];
1450 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++)
1452 if ((cwd
[i
] = DRIVE_GetDosCwd(i
)) && cwd
[i
][0])
1453 length
+= WideCharToMultiByte(DRIVE_GetCodepage(i
), 0,
1454 cwd
[i
], -1, NULL
, 0, NULL
, NULL
) + 7;
1456 if (!(env
= HeapAlloc( GetProcessHeap(), 0, length
+1 ))) return NULL
;
1457 for (i
= 0, p
= env
; i
< MAX_DOS_DRIVES
; i
++)
1459 if (cwd
[i
] && cwd
[i
][0])
1461 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':';
1462 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':'; *p
++ = '\\';
1463 WideCharToMultiByte(DRIVE_GetCodepage(i
), 0, cwd
[i
], -1, p
, 0x7fffffff, NULL
, NULL
);
1472 /***********************************************************************
1473 * GetDiskFreeSpace (KERNEL.422)
1475 BOOL16 WINAPI
GetDiskFreeSpace16( LPCSTR root
, LPDWORD cluster_sectors
,
1476 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1477 LPDWORD total_clusters
)
1479 return GetDiskFreeSpaceA( root
, cluster_sectors
, sector_bytes
,
1480 free_clusters
, total_clusters
);
1484 /***********************************************************************
1485 * GetDiskFreeSpaceW (KERNEL32.@)
1487 * Fails if expression resulting from current drive's dir and "root"
1488 * is not a root dir of the target drive.
1490 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1491 * if the corresponding info is unneeded.
1493 * FIXME: needs to support UNC names from Win95 OSR2 on.
1495 * Behaviour under Win95a:
1496 * CurrDir root result
1497 * "E:\\TEST" "E:" FALSE
1501 * "E:\\TEST" "\\" TRUE
1502 * "E:\\TEST" ":\\" FALSE
1503 * "E:\\TEST" "E:\\" TRUE
1504 * "E:\\TEST" "" FALSE
1505 * "E:\\" "" FALSE (!)
1507 * "E:\\TEST" 0x0 TRUE (!)
1508 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1509 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1511 BOOL WINAPI
GetDiskFreeSpaceW( LPCWSTR root
, LPDWORD cluster_sectors
,
1512 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1513 LPDWORD total_clusters
)
1515 int drive
, sec_size
;
1516 ULARGE_INTEGER size
,available
;
1520 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root
), cluster_sectors
, sector_bytes
,
1521 free_clusters
, total_clusters
);
1523 if (!root
|| root
[0] == '\\' || root
[0] == '/')
1524 drive
= DRIVE_GetCurrentDrive();
1526 if (root
[0] && root
[1] == ':') /* root contains drive tag */
1528 drive
= toupperW(root
[0]) - 'A';
1530 if (path
[0] == '\0')
1532 path
= DRIVE_GetDosCwd(drive
);
1535 SetLastError(ERROR_PATH_NOT_FOUND
);
1540 if (path
[0] == '\\')
1543 if (path
[0]) /* oops, we are in a subdir */
1545 SetLastError(ERROR_INVALID_NAME
);
1552 SetLastError(ERROR_PATH_NOT_FOUND
);
1554 SetLastError(ERROR_INVALID_NAME
);
1558 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1560 /* Cap the size and available at 2GB as per specs. */
1561 if ((size
.s
.HighPart
) ||(size
.s
.LowPart
> 0x7fffffff))
1563 size
.s
.HighPart
= 0;
1564 size
.s
.LowPart
= 0x7fffffff;
1566 if ((available
.s
.HighPart
) ||(available
.s
.LowPart
> 0x7fffffff))
1568 available
.s
.HighPart
=0;
1569 available
.s
.LowPart
= 0x7fffffff;
1571 sec_size
= (DRIVE_GetType(drive
)==DRIVE_CDROM
) ? 2048 : 512;
1572 size
.s
.LowPart
/= sec_size
;
1573 available
.s
.LowPart
/= sec_size
;
1574 /* FIXME: probably have to adjust those variables too for CDFS */
1576 while (cluster_sec
* 65536 < size
.s
.LowPart
) cluster_sec
*= 2;
1578 if (cluster_sectors
)
1579 *cluster_sectors
= cluster_sec
;
1581 *sector_bytes
= sec_size
;
1583 *free_clusters
= available
.s
.LowPart
/ cluster_sec
;
1585 *total_clusters
= size
.s
.LowPart
/ cluster_sec
;
1590 /***********************************************************************
1591 * GetDiskFreeSpaceA (KERNEL32.@)
1593 BOOL WINAPI
GetDiskFreeSpaceA( LPCSTR root
, LPDWORD cluster_sectors
,
1594 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1595 LPDWORD total_clusters
)
1597 UNICODE_STRING rootW
;
1602 if(!RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
1604 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1609 rootW
.Buffer
= NULL
;
1611 ret
= GetDiskFreeSpaceW(rootW
.Buffer
, cluster_sectors
, sector_bytes
,
1612 free_clusters
, total_clusters
);
1613 RtlFreeUnicodeString(&rootW
);
1619 /***********************************************************************
1620 * GetDiskFreeSpaceExW (KERNEL32.@)
1622 * This function is used to acquire the size of the available and
1623 * total space on a logical volume.
1627 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1628 * detailed error information.
1631 BOOL WINAPI
GetDiskFreeSpaceExW( LPCWSTR root
,
1632 PULARGE_INTEGER avail
,
1633 PULARGE_INTEGER total
,
1634 PULARGE_INTEGER totalfree
)
1637 ULARGE_INTEGER size
,available
;
1639 if (!root
) drive
= DRIVE_GetCurrentDrive();
1641 { /* C: always works for GetDiskFreeSpaceEx */
1642 if ((root
[1]) && ((root
[1] != ':') || (root
[2] && root
[2] != '\\')))
1644 FIXME("there are valid root names which are not supported yet\n");
1645 /* ..like UNC names, for instance. */
1647 WARN("invalid root '%s'\n", debugstr_w(root
));
1650 drive
= toupperW(root
[0]) - 'A';
1653 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1657 total
->s
.HighPart
= size
.s
.HighPart
;
1658 total
->s
.LowPart
= size
.s
.LowPart
;
1663 totalfree
->s
.HighPart
= available
.s
.HighPart
;
1664 totalfree
->s
.LowPart
= available
.s
.LowPart
;
1669 if (FIXME_ON(dosfs
))
1671 /* On Windows2000, we need to check the disk quota
1672 allocated for the user owning the calling process. We
1673 don't want to be more obtrusive than necessary with the
1674 FIXME messages, so don't print the FIXME unless Wine is
1675 actually masquerading as Windows2000. */
1678 ovi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
1679 if (GetVersionExA(&ovi
))
1681 if (ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
&& ovi
.dwMajorVersion
> 4)
1682 FIXME("no per-user quota support yet\n");
1686 /* Quick hack, should eventually be fixed to work 100% with
1687 Windows2000 (see comment above). */
1688 avail
->s
.HighPart
= available
.s
.HighPart
;
1689 avail
->s
.LowPart
= available
.s
.LowPart
;
1695 /***********************************************************************
1696 * GetDiskFreeSpaceExA (KERNEL32.@)
1698 BOOL WINAPI
GetDiskFreeSpaceExA( LPCSTR root
, PULARGE_INTEGER avail
,
1699 PULARGE_INTEGER total
,
1700 PULARGE_INTEGER totalfree
)
1702 UNICODE_STRING rootW
;
1705 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
1706 else rootW
.Buffer
= NULL
;
1708 ret
= GetDiskFreeSpaceExW( rootW
.Buffer
, avail
, total
, totalfree
);
1710 RtlFreeUnicodeString(&rootW
);
1714 /***********************************************************************
1715 * GetDriveType (KERNEL.136)
1716 * This function returns the type of a drive in Win16.
1717 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1718 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1719 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1720 * do any pseudo-clever changes.
1723 * drivetype DRIVE_xxx
1725 UINT16 WINAPI
GetDriveType16( UINT16 drive
) /* [in] number (NOT letter) of drive */
1727 UINT type
= DRIVE_GetType(drive
);
1728 TRACE("(%c:)\n", 'A' + drive
);
1729 if (type
== DRIVE_CDROM
) type
= DRIVE_REMOTE
;
1734 /***********************************************************************
1735 * GetDriveTypeW (KERNEL32.@)
1737 * Returns the type of the disk drive specified. If root is NULL the
1738 * root of the current directory is used.
1742 * Type of drive (from Win32 SDK):
1744 * DRIVE_UNKNOWN unable to find out anything about the drive
1745 * DRIVE_NO_ROOT_DIR nonexistent root dir
1746 * DRIVE_REMOVABLE the disk can be removed from the machine
1747 * DRIVE_FIXED the disk can not be removed from the machine
1748 * DRIVE_REMOTE network disk
1749 * DRIVE_CDROM CDROM drive
1750 * DRIVE_RAMDISK virtual disk in RAM
1752 UINT WINAPI
GetDriveTypeW(LPCWSTR root
) /* [in] String describing drive */
1755 TRACE("(%s)\n", debugstr_w(root
));
1757 if (NULL
== root
) drive
= DRIVE_GetCurrentDrive();
1760 if ((root
[1]) && (root
[1] != ':'))
1762 WARN("invalid root %s\n", debugstr_w(root
));
1763 return DRIVE_NO_ROOT_DIR
;
1765 drive
= toupperW(root
[0]) - 'A';
1767 return DRIVE_GetType(drive
);
1771 /***********************************************************************
1772 * GetDriveTypeA (KERNEL32.@)
1774 UINT WINAPI
GetDriveTypeA( LPCSTR root
)
1776 UNICODE_STRING rootW
;
1781 if( !RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
1783 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1788 rootW
.Buffer
= NULL
;
1790 ret
= GetDriveTypeW(rootW
.Buffer
);
1792 RtlFreeUnicodeString(&rootW
);
1798 /***********************************************************************
1799 * GetCurrentDirectory (KERNEL.411)
1801 UINT16 WINAPI
GetCurrentDirectory16( UINT16 buflen
, LPSTR buf
)
1803 WCHAR cur_dirW
[MAX_PATH
];
1805 DRIVE_GetCurrentDirectory(MAX_PATH
, cur_dirW
);
1806 return (UINT16
)WideCharToMultiByte(CP_ACP
, 0, cur_dirW
, -1, buf
, buflen
, NULL
, NULL
);
1810 /***********************************************************************
1811 * GetCurrentDirectoryW (KERNEL32.@)
1813 UINT WINAPI
GetCurrentDirectoryW( UINT buflen
, LPWSTR buf
)
1816 WCHAR longname
[MAX_PATHNAME_LEN
];
1817 WCHAR shortname
[MAX_PATHNAME_LEN
];
1819 ret
= DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN
, shortname
);
1820 if ( ret
> MAX_PATHNAME_LEN
) {
1821 ERR_(file
)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret
);
1824 GetLongPathNameW(shortname
, longname
, MAX_PATHNAME_LEN
);
1825 ret
= strlenW( longname
) + 1;
1826 if (ret
> buflen
) return ret
;
1827 strcpyW(buf
, longname
);
1831 /***********************************************************************
1832 * GetCurrentDirectoryA (KERNEL32.@)
1834 UINT WINAPI
GetCurrentDirectoryA( UINT buflen
, LPSTR buf
)
1836 WCHAR bufferW
[MAX_PATH
];
1839 retW
= GetCurrentDirectoryW(MAX_PATH
, bufferW
);
1843 else if (retW
> MAX_PATH
)
1845 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1850 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
1853 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buf
, buflen
, NULL
, NULL
);
1854 ret
--; /* length without 0 */
1861 /***********************************************************************
1862 * SetCurrentDirectory (KERNEL.412)
1864 BOOL16 WINAPI
SetCurrentDirectory16( LPCSTR dir
)
1866 return SetCurrentDirectoryA( dir
);
1870 /***********************************************************************
1871 * SetCurrentDirectoryW (KERNEL32.@)
1873 BOOL WINAPI
SetCurrentDirectoryW( LPCWSTR dir
)
1875 int drive
, olddrive
= DRIVE_GetCurrentDrive();
1879 SetLastError(ERROR_INVALID_PARAMETER
);
1882 if (dir
[0] && (dir
[1]==':'))
1884 drive
= toupperW( *dir
) - 'A';
1890 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1891 sets pTask->curdir only if pTask->curdrive is drive */
1892 if (!(DRIVE_SetCurrentDrive( drive
)))
1895 /* FIXME: what about empty strings? Add a \\ ? */
1896 if (!DRIVE_Chdir( drive
, dir
)) {
1897 DRIVE_SetCurrentDrive(olddrive
);
1904 /***********************************************************************
1905 * SetCurrentDirectoryA (KERNEL32.@)
1907 BOOL WINAPI
SetCurrentDirectoryA( LPCSTR dir
)
1909 UNICODE_STRING dirW
;
1914 SetLastError(ERROR_INVALID_PARAMETER
);
1918 if (RtlCreateUnicodeStringFromAsciiz(&dirW
, dir
))
1920 ret
= SetCurrentDirectoryW(dirW
.Buffer
);
1921 RtlFreeUnicodeString(&dirW
);
1924 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1929 /***********************************************************************
1930 * GetLogicalDriveStringsA (KERNEL32.@)
1932 UINT WINAPI
GetLogicalDriveStringsA( UINT len
, LPSTR buffer
)
1936 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1937 if (DRIVE_IsValid(drive
)) count
++;
1938 if ((count
* 4) + 1 <= len
)
1941 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1942 if (DRIVE_IsValid(drive
))
1953 return (count
* 4) + 1; /* account for terminating null */
1954 /* The API tells about these different return values */
1958 /***********************************************************************
1959 * GetLogicalDriveStringsW (KERNEL32.@)
1961 UINT WINAPI
GetLogicalDriveStringsW( UINT len
, LPWSTR buffer
)
1965 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1966 if (DRIVE_IsValid(drive
)) count
++;
1967 if (count
* 4 * sizeof(WCHAR
) <= len
)
1970 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1971 if (DRIVE_IsValid(drive
))
1973 *p
++ = (WCHAR
)('a' + drive
);
1980 return count
* 4 * sizeof(WCHAR
);
1984 /***********************************************************************
1985 * GetLogicalDrives (KERNEL32.@)
1987 DWORD WINAPI
GetLogicalDrives(void)
1992 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1994 if ( (DRIVE_IsValid(drive
)) ||
1995 (DOSDrives
[drive
].type
== DRIVE_CDROM
)) /* audio CD is also valid */
1996 ret
|= (1 << drive
);
2002 /***********************************************************************
2003 * GetVolumeInformationW (KERNEL32.@)
2005 BOOL WINAPI
GetVolumeInformationW( LPCWSTR root
, LPWSTR label
,
2006 DWORD label_len
, DWORD
*serial
,
2007 DWORD
*filename_len
, DWORD
*flags
,
2008 LPWSTR fsname
, DWORD fsname_len
)
2013 /* FIXME, SetLastError()s missing */
2015 if (!root
) drive
= DRIVE_GetCurrentDrive();
2018 if (root
[0] && root
[1] != ':')
2020 WARN("invalid root %s\n", debugstr_w(root
));
2023 drive
= toupperW(root
[0]) - 'A';
2025 if (!DRIVE_IsValid( drive
)) return FALSE
;
2026 if (label
&& label_len
)
2028 strncpyW( label
, DRIVE_GetLabel(drive
), label_len
);
2029 label
[label_len
- 1] = 0; /* ensure 0 termination */
2030 cp
= label
+ strlenW(label
);
2031 while (cp
!= label
&& *(cp
-1) == ' ') cp
--;
2034 if (serial
) *serial
= DRIVE_GetSerialNumber(drive
);
2036 /* Set the filesystem information */
2037 /* Note: we only emulate a FAT fs at present */
2040 if (DOSDrives
[drive
].flags
& DRIVE_SHORT_NAMES
)
2043 *filename_len
= 255;
2048 if (DOSDrives
[drive
].flags
& DRIVE_CASE_SENSITIVE
)
2049 *flags
|=FS_CASE_SENSITIVE
;
2050 if (DOSDrives
[drive
].flags
& DRIVE_CASE_PRESERVING
)
2051 *flags
|=FS_CASE_IS_PRESERVED
;
2053 if (fsname
&& fsname_len
)
2055 /* Diablo checks that return code ... */
2056 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
2058 static const WCHAR cdfsW
[] = {'C','D','F','S',0};
2059 strncpyW( fsname
, cdfsW
, fsname_len
);
2063 static const WCHAR fatW
[] = {'F','A','T',0};
2064 strncpyW( fsname
, fatW
, fsname_len
);
2066 fsname
[fsname_len
- 1] = 0; /* ensure 0 termination */
2072 /***********************************************************************
2073 * GetVolumeInformationA (KERNEL32.@)
2075 BOOL WINAPI
GetVolumeInformationA( LPCSTR root
, LPSTR label
,
2076 DWORD label_len
, DWORD
*serial
,
2077 DWORD
*filename_len
, DWORD
*flags
,
2078 LPSTR fsname
, DWORD fsname_len
)
2080 UNICODE_STRING rootW
;
2081 LPWSTR labelW
, fsnameW
;
2084 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
2085 else rootW
.Buffer
= NULL
;
2086 labelW
= label
? HeapAlloc(GetProcessHeap(), 0, label_len
* sizeof(WCHAR
)) : NULL
;
2087 fsnameW
= fsname
? HeapAlloc(GetProcessHeap(), 0, fsname_len
* sizeof(WCHAR
)) : NULL
;
2089 if ((ret
= GetVolumeInformationW(rootW
.Buffer
, labelW
, label_len
, serial
,
2090 filename_len
, flags
, fsnameW
, fsname_len
)))
2092 if (label
) WideCharToMultiByte(CP_ACP
, 0, labelW
, -1, label
, label_len
, NULL
, NULL
);
2093 if (fsname
) WideCharToMultiByte(CP_ACP
, 0, fsnameW
, -1, fsname
, fsname_len
, NULL
, NULL
);
2096 RtlFreeUnicodeString(&rootW
);
2097 if (labelW
) HeapFree( GetProcessHeap(), 0, labelW
);
2098 if (fsnameW
) HeapFree( GetProcessHeap(), 0, fsnameW
);
2102 /***********************************************************************
2103 * SetVolumeLabelW (KERNEL32.@)
2105 BOOL WINAPI
SetVolumeLabelW( LPCWSTR root
, LPCWSTR volname
)
2109 /* FIXME, SetLastErrors missing */
2111 if (!root
) drive
= DRIVE_GetCurrentDrive();
2114 if ((root
[1]) && (root
[1] != ':'))
2116 WARN("invalid root %s\n", debugstr_w(root
));
2119 drive
= toupperW(root
[0]) - 'A';
2121 if (!DRIVE_IsValid( drive
)) return FALSE
;
2123 /* some copy protection stuff check this */
2124 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return FALSE
;
2126 strncpyW(DOSDrives
[drive
].label_conf
, volname
, 12);
2127 DOSDrives
[drive
].label_conf
[12 - 1] = 0; /* ensure 0 termination */
2131 /***********************************************************************
2132 * SetVolumeLabelA (KERNEL32.@)
2134 BOOL WINAPI
SetVolumeLabelA(LPCSTR root
, LPCSTR volname
)
2136 UNICODE_STRING rootW
, volnameW
;
2139 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
2140 else rootW
.Buffer
= NULL
;
2141 if (volname
) RtlCreateUnicodeStringFromAsciiz(&volnameW
, volname
);
2142 else volnameW
.Buffer
= NULL
;
2144 ret
= SetVolumeLabelW( rootW
.Buffer
, volnameW
.Buffer
);
2146 RtlFreeUnicodeString(&rootW
);
2147 RtlFreeUnicodeString(&volnameW
);
2151 /***********************************************************************
2152 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
2154 DWORD WINAPI
GetVolumeNameForVolumeMountPointW(LPWSTR str
, DWORD a
, DWORD b
)
2156 FIXME("(%s, %lx, %lx): stub\n", debugstr_w(str
), a
, b
);