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 extern void CDROM_InitRegistry(int dev
);
143 /***********************************************************************
146 static UINT
DRIVE_GetDriveType( LPCWSTR name
)
150 static const WCHAR TypeW
[] = {'T','y','p','e',0};
151 static const WCHAR hdW
[] = {'h','d',0};
153 PROFILE_GetWineIniString( name
, TypeW
, hdW
, buffer
, 20 );
156 for (i
= 0; i
< sizeof(DRIVE_Types
)/sizeof(DRIVE_Types
[0]); i
++)
158 if (!strcmpiW( buffer
, DRIVE_Types
[i
] )) return i
;
160 MESSAGE("%s: unknown drive type %s, defaulting to 'hd'.\n",
161 debugstr_w(name
), debugstr_w(buffer
) );
166 /***********************************************************************
169 static UINT
DRIVE_GetFSFlags( LPCWSTR name
, LPCWSTR value
)
171 const FS_DESCR
*descr
;
173 for (descr
= DRIVE_Filesystems
; *descr
->name
; descr
++)
174 if (!strcmpiW( value
, descr
->name
)) return descr
->flags
;
175 MESSAGE("%s: unknown filesystem type %s, defaulting to 'win95'.\n",
176 debugstr_w(name
), debugstr_w(value
) );
177 return DRIVE_CASE_PRESERVING
;
181 /***********************************************************************
186 int i
, len
, count
= 0;
187 WCHAR name
[] = {'D','r','i','v','e',' ','A',0};
188 WCHAR drive_env
[] = {'=','A',':',0};
189 WCHAR path
[MAX_PATHNAME_LEN
];
191 struct stat drive_stat_buffer
;
194 static const WCHAR PathW
[] = {'P','a','t','h',0};
195 static const WCHAR empty_strW
[] = { 0 };
196 static const WCHAR CodepageW
[] = {'C','o','d','e','p','a','g','e',0};
197 static const WCHAR LabelW
[] = {'L','a','b','e','l',0};
198 static const WCHAR SerialW
[] = {'S','e','r','i','a','l',0};
199 static const WCHAR zeroW
[] = {'0',0};
200 static const WCHAR def_serialW
[] = {'1','2','3','4','5','6','7','8',0};
201 static const WCHAR FilesystemW
[] = {'F','i','l','e','s','y','s','t','e','m',0};
202 static const WCHAR win95W
[] = {'w','i','n','9','5',0};
203 static const WCHAR DeviceW
[] = {'D','e','v','i','c','e',0};
204 static const WCHAR ReadVolInfoW
[] = {'R','e','a','d','V','o','l','I','n','f','o',0};
205 static const WCHAR FailReadOnlyW
[] = {'F','a','i','l','R','e','a','d','O','n','l','y',0};
206 static const WCHAR driveC_labelW
[] = {'D','r','i','v','e',' ','C',' ',' ',' ',' ',0};
208 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, name
[6]++, drive
++)
210 PROFILE_GetWineIniString( name
, PathW
, empty_strW
, path
, MAX_PATHNAME_LEN
);
213 /* Get the code page number */
214 PROFILE_GetWineIniString( name
, CodepageW
, zeroW
, /* 0 == CP_ACP */
216 drive
->codepage
= strtolW( buffer
, NULL
, 10 );
218 p
= path
+ strlenW(path
) - 1;
219 while ((p
> path
) && (*p
== '/')) *p
-- = '\0';
223 len
= WideCharToMultiByte(drive
->codepage
, 0, path
, -1, NULL
, 0, NULL
, NULL
);
224 drive
->root
= HeapAlloc(GetProcessHeap(), 0, len
);
225 WideCharToMultiByte(drive
->codepage
, 0, path
, -1, drive
->root
, len
, NULL
, NULL
);
229 /* relative paths are relative to config dir */
230 const char *config
= wine_get_config_dir();
231 len
= strlen(config
);
232 len
+= WideCharToMultiByte(drive
->codepage
, 0, path
, -1, NULL
, 0, NULL
, NULL
) + 2;
233 drive
->root
= HeapAlloc( GetProcessHeap(), 0, len
);
234 len
-= sprintf( drive
->root
, "%s/", config
);
235 WideCharToMultiByte(drive
->codepage
, 0, path
, -1, drive
->root
+ strlen(drive
->root
), len
, NULL
, NULL
);
238 if (stat( drive
->root
, &drive_stat_buffer
))
240 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
241 drive
->root
, strerror(errno
), 'A' + i
);
242 HeapFree( GetProcessHeap(), 0, drive
->root
);
246 if (!S_ISDIR(drive_stat_buffer
.st_mode
))
248 MESSAGE("%s is not a directory, ignoring drive %c:\n",
249 drive
->root
, 'A' + i
);
250 HeapFree( GetProcessHeap(), 0, drive
->root
);
255 drive
->dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(drive
->dos_cwd
[0]));
256 drive
->unix_cwd
= heap_strdup( "" );
257 drive
->type
= DRIVE_GetDriveType( name
);
258 drive
->device
= NULL
;
260 drive
->dev
= drive_stat_buffer
.st_dev
;
261 drive
->ino
= drive_stat_buffer
.st_ino
;
263 /* Get the drive label */
264 PROFILE_GetWineIniString( name
, LabelW
, empty_strW
, drive
->label_conf
, 12 );
265 if ((len
= strlenW(drive
->label_conf
)) < 11)
267 /* Pad label with spaces */
268 while(len
< 11) drive
->label_conf
[len
++] = ' ';
269 drive
->label_conf
[11] = '\0';
272 /* Get the serial number */
273 PROFILE_GetWineIniString( name
, SerialW
, def_serialW
, buffer
, 80 );
274 drive
->serial_conf
= strtoulW( buffer
, NULL
, 16 );
276 /* Get the filesystem type */
277 PROFILE_GetWineIniString( name
, FilesystemW
, win95W
, buffer
, 80 );
278 drive
->flags
= DRIVE_GetFSFlags( name
, buffer
);
281 PROFILE_GetWineIniString( name
, DeviceW
, empty_strW
, buffer
, 80 );
285 len
= WideCharToMultiByte(CP_ACP
, 0, buffer
, -1, NULL
, 0, NULL
, NULL
);
286 drive
->device
= HeapAlloc(GetProcessHeap(), 0, len
);
287 WideCharToMultiByte(drive
->codepage
, 0, buffer
, -1, drive
->device
, len
, NULL
, NULL
);
289 if (PROFILE_GetWineIniBool( name
, ReadVolInfoW
, 1))
290 drive
->flags
|= DRIVE_READ_VOL_INFO
;
292 if (drive
->type
== DRIVE_CDROM
)
294 if ((cd_fd
= open(drive
->device
, O_RDONLY
|O_NONBLOCK
)) != -1)
296 CDROM_InitRegistry(cd_fd
);
302 /* Get the FailReadOnly flag */
303 if (PROFILE_GetWineIniBool( name
, FailReadOnlyW
, 0 ))
304 drive
->flags
|= DRIVE_FAIL_READ_ONLY
;
306 /* Make the first hard disk the current drive */
307 if ((DRIVE_CurDrive
== -1) && (drive
->type
== DRIVE_FIXED
))
311 TRACE("%s: path=%s type=%s label=%s serial=%08lx "
312 "flags=%08x codepage=%u dev=%x ino=%x\n",
313 debugstr_w(name
), drive
->root
, debugstr_w(DRIVE_Types
[drive
->type
]),
314 debugstr_w(drive
->label_conf
), drive
->serial_conf
, drive
->flags
,
315 drive
->codepage
, (int)drive
->dev
, (int)drive
->ino
);
317 else WARN("%s: not defined\n", debugstr_w(name
) );
322 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
323 /* Create a C drive pointing to Unix root dir */
324 DOSDrives
[2].root
= heap_strdup( "/" );
325 DOSDrives
[2].dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(DOSDrives
[2].dos_cwd
[0]));
326 DOSDrives
[2].unix_cwd
= heap_strdup( "" );
327 strcpyW( DOSDrives
[2].label_conf
, driveC_labelW
);
328 DOSDrives
[2].serial_conf
= 12345678;
329 DOSDrives
[2].type
= DRIVE_FIXED
;
330 DOSDrives
[2].device
= NULL
;
331 DOSDrives
[2].flags
= 0;
335 /* Make sure the current drive is valid */
336 if (DRIVE_CurDrive
== -1)
338 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
340 if (drive
->root
&& !(drive
->flags
& DRIVE_DISABLED
))
348 /* get current working directory info for all drives */
349 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++, drive_env
[1]++)
351 if (!GetEnvironmentVariableW(drive_env
, path
, MAX_PATHNAME_LEN
)) continue;
353 if (toupperW(path
[0]) != drive_env
[1] || path
[1] != ':') continue;
354 DRIVE_Chdir( i
, path
+ 2 );
360 /***********************************************************************
363 int DRIVE_IsValid( int drive
)
365 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
366 return (DOSDrives
[drive
].root
&&
367 !(DOSDrives
[drive
].flags
& DRIVE_DISABLED
));
371 /***********************************************************************
372 * DRIVE_GetCurrentDrive
374 int DRIVE_GetCurrentDrive(void)
376 TDB
*pTask
= TASK_GetCurrent();
377 if (pTask
&& (pTask
->curdrive
& 0x80)) return pTask
->curdrive
& ~0x80;
378 return DRIVE_CurDrive
;
382 /***********************************************************************
383 * DRIVE_SetCurrentDrive
385 int DRIVE_SetCurrentDrive( int drive
)
387 TDB
*pTask
= TASK_GetCurrent();
388 if (!DRIVE_IsValid( drive
))
390 SetLastError( ERROR_INVALID_DRIVE
);
393 TRACE("%c:\n", 'A' + drive
);
394 DRIVE_CurDrive
= drive
;
395 if (pTask
) pTask
->curdrive
= drive
| 0x80;
400 /***********************************************************************
401 * DRIVE_FindDriveRoot
403 * Find a drive for which the root matches the beginning of the given path.
404 * This can be used to translate a Unix path into a drive + DOS path.
405 * Return value is the drive, or -1 on error. On success, path is modified
406 * to point to the beginning of the DOS path.
408 * Note: path must be in the encoding of the underlying Unix file system.
410 int DRIVE_FindDriveRoot( const char **path
)
412 /* Starting with the full path, check if the device and inode match any of
413 * the wine 'drives'. If not then remove the last path component and try
414 * again. If the last component was a '..' then skip a normal component
415 * since it's a directory that's ascended back out of.
417 int drive
, level
, len
;
418 char buffer
[MAX_PATHNAME_LEN
];
422 strcpy( buffer
, *path
);
423 while ((p
= strchr( buffer
, '\\' )) != NULL
)
425 len
= strlen(buffer
);
427 /* strip off trailing slashes */
428 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
433 if (stat( buffer
, &st
) == 0 && S_ISDIR( st
.st_mode
))
435 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
437 if (!DOSDrives
[drive
].root
||
438 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
))
441 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
442 (DOSDrives
[drive
].ino
== st
.st_ino
))
444 if (len
== 1) len
= 0; /* preserve root slash in returned path */
445 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
446 *path
, 'A' + drive
, buffer
, *path
+ len
);
448 if (!**path
) *path
= "\\";
453 if (len
<= 1) return -1; /* reached root */
458 /* find start of the last path component */
459 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
460 if (!buffer
[len
]) break; /* empty component -> reached root */
461 /* does removing it take us up a level? */
462 if (strcmp( buffer
+ len
, "." ) != 0)
463 level
+= strcmp( buffer
+ len
, ".." ) ? 1 : -1;
465 /* strip off trailing slashes */
466 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
472 /***********************************************************************
473 * DRIVE_FindDriveRootW
475 * Unicode version of DRIVE_FindDriveRoot.
477 int DRIVE_FindDriveRootW( LPCWSTR
*path
)
479 int drive
, level
, len
;
480 WCHAR buffer
[MAX_PATHNAME_LEN
];
484 strcpyW( buffer
, *path
);
485 while ((p
= strchrW( buffer
, '\\' )) != NULL
)
487 len
= strlenW(buffer
);
489 /* strip off trailing slashes */
490 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
497 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
499 char buffA
[MAX_PATHNAME_LEN
];
501 if (!DOSDrives
[drive
].root
||
502 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
))
505 if (codepage
!= DOSDrives
[drive
].codepage
)
507 WideCharToMultiByte( DOSDrives
[drive
].codepage
, 0, buffer
, -1,
508 buffA
, sizeof(buffA
), NULL
, NULL
);
509 if (stat( buffA
, &st
) == -1 || !S_ISDIR( st
.st_mode
))
514 codepage
= DOSDrives
[drive
].codepage
;
517 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
518 (DOSDrives
[drive
].ino
== st
.st_ino
))
520 static const WCHAR rootW
[] = {'\\',0};
522 if (len
== 1) len
= 0; /* preserve root slash in returned path */
523 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
524 debugstr_w(*path
), 'A' + drive
, debugstr_w(buffer
), debugstr_w(*path
+ len
));
526 if (!**path
) *path
= rootW
;
530 if (len
<= 1) return -1; /* reached root */
535 static const WCHAR dotW
[] = {'.',0};
536 static const WCHAR dotdotW
[] = {'.','.',0};
538 /* find start of the last path component */
539 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
540 if (!buffer
[len
]) break; /* empty component -> reached root */
541 /* does removing it take us up a level? */
542 if (strcmpW( buffer
+ len
, dotW
) != 0)
543 level
+= strcmpW( buffer
+ len
, dotdotW
) ? 1 : -1;
545 /* strip off trailing slashes */
546 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
552 /***********************************************************************
555 const char * DRIVE_GetRoot( int drive
)
557 if (!DRIVE_IsValid( drive
)) return NULL
;
558 return DOSDrives
[drive
].root
;
562 /***********************************************************************
565 LPCWSTR
DRIVE_GetDosCwd( int drive
)
567 TDB
*pTask
= TASK_GetCurrent();
568 if (!DRIVE_IsValid( drive
)) return NULL
;
570 /* Check if we need to change the directory to the new task. */
571 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
572 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
573 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
575 static const WCHAR rootW
[] = {'\\',0};
576 WCHAR curdirW
[MAX_PATH
];
577 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
578 /* Perform the task-switch */
579 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
580 DRIVE_LastTask
= GetCurrentTask();
582 return DOSDrives
[drive
].dos_cwd
;
586 /***********************************************************************
589 const char * DRIVE_GetUnixCwd( int drive
)
591 TDB
*pTask
= TASK_GetCurrent();
592 if (!DRIVE_IsValid( drive
)) return NULL
;
594 /* Check if we need to change the directory to the new task. */
595 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
596 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
597 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
599 static const WCHAR rootW
[] = {'\\',0};
600 WCHAR curdirW
[MAX_PATH
];
601 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
602 /* Perform the task-switch */
603 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
604 DRIVE_LastTask
= GetCurrentTask();
606 return DOSDrives
[drive
].unix_cwd
;
610 /***********************************************************************
613 const char * DRIVE_GetDevice( int drive
)
615 return (DRIVE_IsValid( drive
)) ? DOSDrives
[drive
].device
: NULL
;
618 /******************************************************************
619 * static WORD CDROM_Data_FindBestVoldesc
623 static WORD
CDROM_Data_FindBestVoldesc(int fd
)
625 BYTE cur_vd_type
, max_vd_type
= 0;
626 unsigned int offs
, best_offs
= 0, extra_offs
= 0;
629 for (offs
= 0x8000; offs
<= 0x9800; offs
+= 0x800)
631 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
632 * the volume label is displaced forward by 8
634 lseek(fd
, offs
+ 11, SEEK_SET
); /* check for non-ISO9660 signature */
636 if ((sig
[0] == 'R') && (sig
[1] == 'O') && (sig
[2]=='M'))
640 lseek(fd
, offs
+ extra_offs
, SEEK_SET
);
641 read(fd
, &cur_vd_type
, 1);
642 if (cur_vd_type
== 0xff) /* voldesc set terminator */
644 if (cur_vd_type
> max_vd_type
)
646 max_vd_type
= cur_vd_type
;
647 best_offs
= offs
+ extra_offs
;
653 /***********************************************************************
654 * DRIVE_ReadSuperblock
657 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
658 * to check, that they are writing on a FAT filesystem !
660 int DRIVE_ReadSuperblock (int drive
, char * buff
)
662 #define DRIVE_SUPER 96
666 struct stat stat_buf
;
668 memset(buff
, 0, DRIVE_SUPER
);
669 /* O_NONBLOCK in case we're opening FIFO; will be reset later */
670 if ((fd
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NOCTTY
|O_NONBLOCK
)) != -1) {
671 if (fstat(fd
, &stat_buf
) < 0) { /* shouldn't happen since we just opened that file */
672 ERR("fstat() failed for opened device '%s' (drive %c:) ! IT SHOULDN'T HAPPEN !!!\n",
673 DOSDrives
[drive
].device
, 'A'+drive
);
675 } else if (!S_ISBLK(stat_buf
.st_mode
)) {
676 ERR("Device '%s' (drive %c:) is not a block device - check your config\n",
677 DOSDrives
[drive
].device
, 'A'+drive
);
679 /* reset O_NONBLOCK */
680 } else if (fcntl(fd
, F_SETFL
, 0) < 0 || fcntl(fd
, F_GETFL
) & O_NONBLOCK
) {
681 ERR("fcntl() failed to reset O_NONBLOCK for device '%s' (drive %c:)\n",
682 DOSDrives
[drive
].device
, 'A'+drive
);
690 if (!DOSDrives
[drive
].device
)
691 ERR("No device configured for drive %c: !\n", 'A'+drive
);
693 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives
[drive
].device
, 'A'+drive
,
694 (stat(DOSDrives
[drive
].device
, &stat_buf
)) ?
695 "not available or symlink not valid ?" : "no permission");
698 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
699 PROFILE_UsageWineIni();
703 switch(DOSDrives
[drive
].type
)
705 case DRIVE_REMOVABLE
:
710 offs
= CDROM_Data_FindBestVoldesc(fd
);
717 if ((offs
) && (lseek(fd
,offs
,SEEK_SET
)!=offs
))
722 if (read(fd
,buff
,DRIVE_SUPER
)!=DRIVE_SUPER
)
728 switch(DOSDrives
[drive
].type
)
730 case DRIVE_REMOVABLE
:
732 if ((buff
[0x26]!=0x29) || /* Check for FAT present */
733 /* FIXME: do really all FAT have their name beginning with
734 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
735 memcmp( buff
+0x36,"FAT",3))
737 ERR("The filesystem is not FAT !! (device=%s)\n",
738 DOSDrives
[drive
].device
);
744 if (strncmp(&buff
[1],"CD001",5)) /* Check for iso9660 present */
749 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
763 /***********************************************************************
764 * DRIVE_WriteSuperblockEntry
767 * We are writing as little as possible (ie. not the whole SuperBlock)
768 * not to interfere with kernel. The drive can be mounted !
770 int DRIVE_WriteSuperblockEntry (int drive
, off_t ofs
, size_t len
, char * buff
)
774 if ((fd
=open(DOSDrives
[drive
].device
,O_WRONLY
))==-1)
776 ERR("Cannot open the device %s (for writing)\n",
777 DOSDrives
[drive
].device
);
780 if (lseek(fd
,ofs
,SEEK_SET
)!=ofs
)
782 ERR("lseek failed on device %s !\n",
783 DOSDrives
[drive
].device
);
787 if (write(fd
,buff
,len
)!=len
)
790 ERR("Cannot write on %s !\n",
791 DOSDrives
[drive
].device
);
797 /******************************************************************
798 * static HANDLE CDROM_Open
802 static HANDLE
CDROM_Open(int drive
)
804 WCHAR root
[] = {'\\','\\','.','\\','A',':',0};
806 return CreateFileW(root
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0);
809 /**************************************************************************
810 * CDROM_Data_GetLabel [internal]
812 DWORD
CDROM_Data_GetLabel(int drive
, WCHAR
*label
)
814 #define LABEL_LEN 32+1
815 int dev
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NONBLOCK
);
816 WORD offs
= CDROM_Data_FindBestVoldesc(dev
);
817 WCHAR label_read
[LABEL_LEN
]; /* Unicode possible, too */
818 DWORD unicode_id
= 0;
822 if ((lseek(dev
, offs
+0x58, SEEK_SET
) == offs
+0x58)
823 && (read(dev
, &unicode_id
, 3) == 3))
825 int ver
= (unicode_id
& 0xff0000) >> 16;
827 if ((lseek(dev
, offs
+0x28, SEEK_SET
) != offs
+0x28)
828 || (read(dev
, &label_read
, LABEL_LEN
) != LABEL_LEN
))
832 if ((LOWORD(unicode_id
) == 0x2f25) /* Unicode ID */
833 && ((ver
== 0x40) || (ver
== 0x43) || (ver
== 0x45)))
834 { /* yippee, unicode */
837 for (i
=0; i
<LABEL_LEN
;i
++)
838 { /* Motorola -> Intel Unicode conversion :-\ */
840 label_read
[i
] = (ch
<< 8) | (ch
>> 8);
842 strncpyW(label
, label_read
, 11);
847 MultiByteToWideChar(DOSDrives
[drive
].codepage
, 0, (LPSTR
)label_read
, -1, label
, 11);
855 ERR("error reading label !\n");
859 /**************************************************************************
860 * CDROM_GetLabel [internal]
862 static DWORD
CDROM_GetLabel(int drive
, WCHAR
*label
)
864 HANDLE h
= CDROM_Open(drive
);
869 if (!h
|| !DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
, 0, &cdd
, sizeof(cdd
), &br
, 0))
872 switch (cdd
.DiskData
& 0x03)
874 case CDROM_DISK_DATA_TRACK
:
875 if (!CDROM_Data_GetLabel(drive
, label
))
878 case CDROM_DISK_AUDIO_TRACK
:
880 static const WCHAR audioCD
[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
881 strcpyW(label
, audioCD
);
884 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
885 FIXME("Need to get the label of a mixed mode CD!\n");
886 /* This assumes that the first track is a data track! */
887 /* I guess the correct way would be to enumerate all data tracks
888 and check each for the title */
889 if (!CDROM_Data_GetLabel(drive
, label
))
896 TRACE("CD: label is %s\n", debugstr_w(label
));
900 /***********************************************************************
903 LPCWSTR
DRIVE_GetLabel( int drive
)
906 char buff
[DRIVE_SUPER
];
909 if (!DRIVE_IsValid( drive
)) return NULL
;
910 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
912 read
= CDROM_GetLabel(drive
, DOSDrives
[drive
].label_read
);
915 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
917 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
918 ERR("Invalid or unreadable superblock on %s (%c:).\n",
919 DOSDrives
[drive
].device
, (char)(drive
+'A'));
921 if (DOSDrives
[drive
].type
== DRIVE_REMOVABLE
||
922 DOSDrives
[drive
].type
== DRIVE_FIXED
)
925 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
927 MultiByteToWideChar(DOSDrives
[drive
].codepage
, 0, buff
+offs
, 11,
928 DOSDrives
[drive
].label_read
, 11);
929 DOSDrives
[drive
].label_read
[11]='\0';
935 DOSDrives
[drive
].label_read
: DOSDrives
[drive
].label_conf
;
938 #define CDFRAMES_PERSEC 75
939 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
940 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
941 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
943 /**************************************************************************
944 * CDROM_Audio_GetSerial [internal]
946 static DWORD
CDROM_Audio_GetSerial(HANDLE h
)
948 unsigned long serial
= 0;
951 DWORD dwStart
, dwEnd
, br
;
954 if (!DeviceIoControl(h
, IOCTL_CDROM_READ_TOC
, NULL
, 0, &toc
, sizeof(toc
), &br
, 0))
958 * wMagic collects the wFrames from track 1
959 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
961 * There it is collected for correcting the serial when there are less than
964 wMagic
= toc
.TrackData
[0].Address
[2];
965 dwStart
= FRAME_OF_TOC(toc
, toc
.FirstTrack
);
967 for (i
= 0; i
<= toc
.LastTrack
- toc
.FirstTrack
; i
++) {
968 serial
+= (toc
.TrackData
[i
].Address
[0] << 16) |
969 (toc
.TrackData
[i
].Address
[1] << 8) | toc
.TrackData
[i
].Address
[2];
971 dwEnd
= FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1);
973 if (toc
.LastTrack
- toc
.FirstTrack
+ 1 < 3)
974 serial
+= wMagic
+ (dwEnd
- dwStart
);
979 /**************************************************************************
980 * CDROM_Data_GetSerial [internal]
982 static DWORD
CDROM_Data_GetSerial(int drive
)
984 int dev
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NONBLOCK
);
990 BYTE b0
= 0, b1
= 1, b2
= 2, b3
= 3;
993 if (dev
== -1) return 0;
994 offs
= CDROM_Data_FindBestVoldesc(dev
);
1003 lseek(dev
, offs
, SEEK_SET
);
1004 read(dev
, buf
, 2048);
1006 * OK, another braindead one... argh. Just believe it.
1007 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
1008 * It's true and nobody will ever be able to change it.
1010 ovi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
1011 GetVersionExA(&ovi
);
1012 if ((ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) && (ovi
.dwMajorVersion
>= 4))
1014 b0
= 3; b1
= 2; b2
= 1; b3
= 0;
1016 for (i
= 0; i
< 2048; i
+= 4)
1018 /* DON'T optimize this into DWORD !! (breaks overflow) */
1019 serial
.p
[b0
] += buf
[i
+b0
];
1020 serial
.p
[b1
] += buf
[i
+b1
];
1021 serial
.p
[b2
] += buf
[i
+b2
];
1022 serial
.p
[b3
] += buf
[i
+b3
];
1029 /**************************************************************************
1030 * CDROM_GetSerial [internal]
1032 static DWORD
CDROM_GetSerial(int drive
)
1035 HANDLE h
= CDROM_Open(drive
);
1036 CDROM_DISK_DATA cdd
;
1039 if (!h
|| ! !DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
, 0, &cdd
, sizeof(cdd
), &br
, 0))
1042 switch (cdd
.DiskData
& 0x03)
1044 case CDROM_DISK_DATA_TRACK
:
1045 /* hopefully a data CD */
1046 serial
= CDROM_Data_GetSerial(drive
);
1048 case CDROM_DISK_AUDIO_TRACK
:
1050 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
1051 serial
= CDROM_Audio_GetSerial(h
);
1058 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial
), LOWORD(serial
));
1065 /***********************************************************************
1066 * DRIVE_GetSerialNumber
1068 DWORD
DRIVE_GetSerialNumber( int drive
)
1071 char buff
[DRIVE_SUPER
];
1073 TRACE("drive %d, type = %d\n", drive
, DOSDrives
[drive
].type
);
1075 if (!DRIVE_IsValid( drive
)) return 0;
1077 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
1079 switch(DOSDrives
[drive
].type
)
1081 case DRIVE_REMOVABLE
:
1083 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
1084 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
1085 " Maybe not FAT?\n" ,
1086 DOSDrives
[drive
].device
, 'A'+drive
);
1088 serial
= *((DWORD
*)(buff
+0x27));
1091 serial
= CDROM_GetSerial(drive
);
1094 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive
+'A');
1098 return (serial
) ? serial
: DOSDrives
[drive
].serial_conf
;
1102 /***********************************************************************
1103 * DRIVE_SetSerialNumber
1105 int DRIVE_SetSerialNumber( int drive
, DWORD serial
)
1107 char buff
[DRIVE_SUPER
];
1109 if (!DRIVE_IsValid( drive
)) return 0;
1111 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
1113 if ((DOSDrives
[drive
].type
!= DRIVE_REMOVABLE
) &&
1114 (DOSDrives
[drive
].type
!= DRIVE_FIXED
)) return 0;
1115 /* check, if the drive has a FAT filesystem */
1116 if (DRIVE_ReadSuperblock(drive
, buff
)) return 0;
1117 if (DRIVE_WriteSuperblockEntry(drive
, 0x27, 4, (char *) &serial
)) return 0;
1121 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return 0;
1122 DOSDrives
[drive
].serial_conf
= serial
;
1127 /***********************************************************************
1130 static UINT
DRIVE_GetType( int drive
)
1132 if (!DRIVE_IsValid( drive
)) return DRIVE_NO_ROOT_DIR
;
1133 return DOSDrives
[drive
].type
;
1137 /***********************************************************************
1140 UINT
DRIVE_GetFlags( int drive
)
1142 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
1143 return DOSDrives
[drive
].flags
;
1146 /***********************************************************************
1149 UINT
DRIVE_GetCodepage( int drive
)
1151 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
1152 return DOSDrives
[drive
].codepage
;
1156 /***********************************************************************
1159 int DRIVE_Chdir( int drive
, LPCWSTR path
)
1161 DOS_FULL_NAME full_name
;
1162 WCHAR buffer
[MAX_PATHNAME_LEN
];
1164 BY_HANDLE_FILE_INFORMATION info
;
1165 TDB
*pTask
= TASK_GetCurrent();
1167 buffer
[0] = 'A' + drive
;
1170 TRACE("(%s,%s)\n", debugstr_w(buffer
), debugstr_w(path
) );
1171 strncpyW( buffer
+ 2, path
, MAX_PATHNAME_LEN
- 2 );
1172 buffer
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1174 if (!DOSFS_GetFullName( buffer
, TRUE
, &full_name
)) return 0;
1175 if (!FILE_Stat( full_name
.long_name
, &info
, NULL
)) return 0;
1176 if (!(info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1178 SetLastError( ERROR_FILE_NOT_FOUND
);
1181 unix_cwd
= full_name
.long_name
+ strlen( DOSDrives
[drive
].root
);
1182 while (*unix_cwd
== '/') unix_cwd
++;
1184 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1185 'A' + drive
, unix_cwd
, debugstr_w(full_name
.short_name
+ 3) );
1187 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].dos_cwd
);
1188 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].unix_cwd
);
1189 DOSDrives
[drive
].dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name
.short_name
) - 2) * sizeof(WCHAR
));
1190 strcpyW(DOSDrives
[drive
].dos_cwd
, full_name
.short_name
+ 3);
1191 DOSDrives
[drive
].unix_cwd
= heap_strdup( unix_cwd
);
1193 if (pTask
&& (pTask
->curdrive
& 0x80) &&
1194 ((pTask
->curdrive
& ~0x80) == drive
))
1196 WideCharToMultiByte(CP_ACP
, 0, full_name
.short_name
+ 2, -1,
1197 pTask
->curdir
, sizeof(pTask
->curdir
), NULL
, NULL
);
1198 DRIVE_LastTask
= GetCurrentTask();
1204 /***********************************************************************
1207 int DRIVE_Disable( int drive
)
1209 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1211 SetLastError( ERROR_INVALID_DRIVE
);
1214 DOSDrives
[drive
].flags
|= DRIVE_DISABLED
;
1219 /***********************************************************************
1222 int DRIVE_Enable( int drive
)
1224 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1226 SetLastError( ERROR_INVALID_DRIVE
);
1229 DOSDrives
[drive
].flags
&= ~DRIVE_DISABLED
;
1234 /***********************************************************************
1235 * DRIVE_SetLogicalMapping
1237 int DRIVE_SetLogicalMapping ( int existing_drive
, int new_drive
)
1239 /* If new_drive is already valid, do nothing and return 0
1240 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1242 DOSDRIVE
*old
, *new;
1244 old
= DOSDrives
+ existing_drive
;
1245 new = DOSDrives
+ new_drive
;
1247 if ((existing_drive
< 0) || (existing_drive
>= MAX_DOS_DRIVES
) ||
1249 (new_drive
< 0) || (new_drive
>= MAX_DOS_DRIVES
))
1251 SetLastError( ERROR_INVALID_DRIVE
);
1257 TRACE("Can't map drive %c: to already existing drive %c:\n",
1258 'A' + existing_drive
, 'A' + new_drive
);
1259 /* it is already mapped there, so return success */
1260 if (!strcmp(old
->root
,new->root
))
1265 new->root
= heap_strdup( old
->root
);
1266 new->dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(old
->dos_cwd
) + 1) * sizeof(WCHAR
));
1267 strcpyW(new->dos_cwd
, old
->dos_cwd
);
1268 new->unix_cwd
= heap_strdup( old
->unix_cwd
);
1269 new->device
= heap_strdup( old
->device
);
1270 memcpy ( new->label_conf
, old
->label_conf
, 12 );
1271 memcpy ( new->label_read
, old
->label_read
, 12 );
1272 new->serial_conf
= old
->serial_conf
;
1273 new->type
= old
->type
;
1274 new->flags
= old
->flags
;
1275 new->dev
= old
->dev
;
1276 new->ino
= old
->ino
;
1278 TRACE("Drive %c: is now equal to drive %c:\n",
1279 'A' + new_drive
, 'A' + existing_drive
);
1285 /***********************************************************************
1288 * Open the drive raw device and return a Unix fd (or -1 on error).
1290 int DRIVE_OpenDevice( int drive
, int flags
)
1292 if (!DRIVE_IsValid( drive
)) return -1;
1293 return open( DOSDrives
[drive
].device
, flags
);
1297 /***********************************************************************
1298 * DRIVE_GetFreeSpace
1300 static int DRIVE_GetFreeSpace( int drive
, PULARGE_INTEGER size
,
1301 PULARGE_INTEGER available
)
1305 if (!DRIVE_IsValid(drive
))
1307 SetLastError( ERROR_PATH_NOT_FOUND
);
1311 /* FIXME: add autoconf check for this */
1312 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1313 if (statfs( DOSDrives
[drive
].root
, &info
, 0, 0) < 0)
1315 if (statfs( DOSDrives
[drive
].root
, &info
) < 0)
1319 WARN("cannot do statfs(%s)\n", DOSDrives
[drive
].root
);
1323 size
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bsize
, info
.f_blocks
);
1324 #ifdef HAVE_STRUCT_STATFS_F_BAVAIL
1325 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bavail
, info
.f_bsize
);
1327 # ifdef HAVE_STRUCT_STATFS_F_BFREE
1328 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bfree
, info
.f_bsize
);
1330 # error "statfs has no bfree/bavail member!"
1333 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
1334 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1335 available
->QuadPart
= 0;
1340 /***********************************************************************
1341 * DRIVE_GetCurrentDirectory
1342 * Returns "X:\\path\\etc\\".
1344 * Despite the API description, return required length including the
1345 * terminating null when buffer too small. This is the real behaviour.
1347 static UINT
DRIVE_GetCurrentDirectory( UINT buflen
, LPWSTR buf
)
1350 LPCWSTR dos_cwd
= DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1351 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
1353 ret
= strlenW(dos_cwd
) + 3; /* length of WHOLE current directory */
1354 if (ret
>= buflen
) return ret
+ 1;
1356 strcpyW( buf
, driveA_rootW
);
1357 buf
[0] += DRIVE_GetCurrentDrive();
1358 strcatW( buf
, dos_cwd
);
1363 /***********************************************************************
1366 * Build the environment array containing the drives' current directories.
1367 * Resulting pointer must be freed with HeapFree.
1369 char *DRIVE_BuildEnv(void)
1372 LPCWSTR cwd
[MAX_DOS_DRIVES
];
1375 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++)
1377 if ((cwd
[i
] = DRIVE_GetDosCwd(i
)) && cwd
[i
][0])
1378 length
+= WideCharToMultiByte(DRIVE_GetCodepage(i
), 0,
1379 cwd
[i
], -1, NULL
, 0, NULL
, NULL
) + 7;
1381 if (!(env
= HeapAlloc( GetProcessHeap(), 0, length
+1 ))) return NULL
;
1382 for (i
= 0, p
= env
; i
< MAX_DOS_DRIVES
; i
++)
1384 if (cwd
[i
] && cwd
[i
][0])
1386 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':';
1387 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':'; *p
++ = '\\';
1388 WideCharToMultiByte(DRIVE_GetCodepage(i
), 0, cwd
[i
], -1, p
, 0x7fffffff, NULL
, NULL
);
1397 /***********************************************************************
1398 * GetDiskFreeSpace (KERNEL.422)
1400 BOOL16 WINAPI
GetDiskFreeSpace16( LPCSTR root
, LPDWORD cluster_sectors
,
1401 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1402 LPDWORD total_clusters
)
1404 return GetDiskFreeSpaceA( root
, cluster_sectors
, sector_bytes
,
1405 free_clusters
, total_clusters
);
1409 /***********************************************************************
1410 * GetDiskFreeSpaceW (KERNEL32.@)
1412 * Fails if expression resulting from current drive's dir and "root"
1413 * is not a root dir of the target drive.
1415 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1416 * if the corresponding info is unneeded.
1418 * FIXME: needs to support UNC names from Win95 OSR2 on.
1420 * Behaviour under Win95a:
1421 * CurrDir root result
1422 * "E:\\TEST" "E:" FALSE
1426 * "E:\\TEST" "\\" TRUE
1427 * "E:\\TEST" ":\\" FALSE
1428 * "E:\\TEST" "E:\\" TRUE
1429 * "E:\\TEST" "" FALSE
1430 * "E:\\" "" FALSE (!)
1432 * "E:\\TEST" 0x0 TRUE (!)
1433 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1434 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1436 BOOL WINAPI
GetDiskFreeSpaceW( LPCWSTR root
, LPDWORD cluster_sectors
,
1437 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1438 LPDWORD total_clusters
)
1440 int drive
, sec_size
;
1441 ULARGE_INTEGER size
,available
;
1445 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root
), cluster_sectors
, sector_bytes
,
1446 free_clusters
, total_clusters
);
1448 if (!root
|| root
[0] == '\\' || root
[0] == '/')
1449 drive
= DRIVE_GetCurrentDrive();
1451 if (root
[0] && root
[1] == ':') /* root contains drive tag */
1453 drive
= toupperW(root
[0]) - 'A';
1455 if (path
[0] == '\0')
1457 path
= DRIVE_GetDosCwd(drive
);
1460 SetLastError(ERROR_PATH_NOT_FOUND
);
1465 if (path
[0] == '\\')
1468 if (path
[0]) /* oops, we are in a subdir */
1470 SetLastError(ERROR_INVALID_NAME
);
1477 SetLastError(ERROR_PATH_NOT_FOUND
);
1479 SetLastError(ERROR_INVALID_NAME
);
1483 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1485 /* Cap the size and available at 2GB as per specs. */
1486 if ((size
.s
.HighPart
) ||(size
.s
.LowPart
> 0x7fffffff))
1488 size
.s
.HighPart
= 0;
1489 size
.s
.LowPart
= 0x7fffffff;
1491 if ((available
.s
.HighPart
) ||(available
.s
.LowPart
> 0x7fffffff))
1493 available
.s
.HighPart
=0;
1494 available
.s
.LowPart
= 0x7fffffff;
1496 sec_size
= (DRIVE_GetType(drive
)==DRIVE_CDROM
) ? 2048 : 512;
1497 size
.s
.LowPart
/= sec_size
;
1498 available
.s
.LowPart
/= sec_size
;
1499 /* FIXME: probably have to adjust those variables too for CDFS */
1501 while (cluster_sec
* 65536 < size
.s
.LowPart
) cluster_sec
*= 2;
1503 if (cluster_sectors
)
1504 *cluster_sectors
= cluster_sec
;
1506 *sector_bytes
= sec_size
;
1508 *free_clusters
= available
.s
.LowPart
/ cluster_sec
;
1510 *total_clusters
= size
.s
.LowPart
/ cluster_sec
;
1515 /***********************************************************************
1516 * GetDiskFreeSpaceA (KERNEL32.@)
1518 BOOL WINAPI
GetDiskFreeSpaceA( LPCSTR root
, LPDWORD cluster_sectors
,
1519 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1520 LPDWORD total_clusters
)
1522 UNICODE_STRING rootW
;
1527 if(!RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
1529 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1534 rootW
.Buffer
= NULL
;
1536 ret
= GetDiskFreeSpaceW(rootW
.Buffer
, cluster_sectors
, sector_bytes
,
1537 free_clusters
, total_clusters
);
1538 RtlFreeUnicodeString(&rootW
);
1544 /***********************************************************************
1545 * GetDiskFreeSpaceExW (KERNEL32.@)
1547 * This function is used to acquire the size of the available and
1548 * total space on a logical volume.
1552 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1553 * detailed error information.
1556 BOOL WINAPI
GetDiskFreeSpaceExW( LPCWSTR root
,
1557 PULARGE_INTEGER avail
,
1558 PULARGE_INTEGER total
,
1559 PULARGE_INTEGER totalfree
)
1562 ULARGE_INTEGER size
,available
;
1564 if (!root
) drive
= DRIVE_GetCurrentDrive();
1566 { /* C: always works for GetDiskFreeSpaceEx */
1567 if ((root
[1]) && ((root
[1] != ':') || (root
[2] && root
[2] != '\\')))
1569 FIXME("there are valid root names which are not supported yet\n");
1570 /* ..like UNC names, for instance. */
1572 WARN("invalid root '%s'\n", debugstr_w(root
));
1575 drive
= toupperW(root
[0]) - 'A';
1578 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1582 total
->s
.HighPart
= size
.s
.HighPart
;
1583 total
->s
.LowPart
= size
.s
.LowPart
;
1588 totalfree
->s
.HighPart
= available
.s
.HighPart
;
1589 totalfree
->s
.LowPart
= available
.s
.LowPart
;
1594 if (FIXME_ON(dosfs
))
1596 /* On Windows2000, we need to check the disk quota
1597 allocated for the user owning the calling process. We
1598 don't want to be more obtrusive than necessary with the
1599 FIXME messages, so don't print the FIXME unless Wine is
1600 actually masquerading as Windows2000. */
1603 ovi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
1604 if (GetVersionExA(&ovi
))
1606 if (ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
&& ovi
.dwMajorVersion
> 4)
1607 FIXME("no per-user quota support yet\n");
1611 /* Quick hack, should eventually be fixed to work 100% with
1612 Windows2000 (see comment above). */
1613 avail
->s
.HighPart
= available
.s
.HighPart
;
1614 avail
->s
.LowPart
= available
.s
.LowPart
;
1620 /***********************************************************************
1621 * GetDiskFreeSpaceExA (KERNEL32.@)
1623 BOOL WINAPI
GetDiskFreeSpaceExA( LPCSTR root
, PULARGE_INTEGER avail
,
1624 PULARGE_INTEGER total
,
1625 PULARGE_INTEGER totalfree
)
1627 UNICODE_STRING rootW
;
1630 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
1631 else rootW
.Buffer
= NULL
;
1633 ret
= GetDiskFreeSpaceExW( rootW
.Buffer
, avail
, total
, totalfree
);
1635 RtlFreeUnicodeString(&rootW
);
1639 /***********************************************************************
1640 * GetDriveType (KERNEL.136)
1641 * This function returns the type of a drive in Win16.
1642 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1643 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1644 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1645 * do any pseudo-clever changes.
1648 * drivetype DRIVE_xxx
1650 UINT16 WINAPI
GetDriveType16( UINT16 drive
) /* [in] number (NOT letter) of drive */
1652 UINT type
= DRIVE_GetType(drive
);
1653 TRACE("(%c:)\n", 'A' + drive
);
1654 if (type
== DRIVE_CDROM
) type
= DRIVE_REMOTE
;
1659 /***********************************************************************
1660 * GetDriveTypeW (KERNEL32.@)
1662 * Returns the type of the disk drive specified. If root is NULL the
1663 * root of the current directory is used.
1667 * Type of drive (from Win32 SDK):
1669 * DRIVE_UNKNOWN unable to find out anything about the drive
1670 * DRIVE_NO_ROOT_DIR nonexistent root dir
1671 * DRIVE_REMOVABLE the disk can be removed from the machine
1672 * DRIVE_FIXED the disk can not be removed from the machine
1673 * DRIVE_REMOTE network disk
1674 * DRIVE_CDROM CDROM drive
1675 * DRIVE_RAMDISK virtual disk in RAM
1677 UINT WINAPI
GetDriveTypeW(LPCWSTR root
) /* [in] String describing drive */
1680 TRACE("(%s)\n", debugstr_w(root
));
1682 if (NULL
== root
) drive
= DRIVE_GetCurrentDrive();
1685 if ((root
[1]) && (root
[1] != ':'))
1687 WARN("invalid root %s\n", debugstr_w(root
));
1688 return DRIVE_NO_ROOT_DIR
;
1690 drive
= toupperW(root
[0]) - 'A';
1692 return DRIVE_GetType(drive
);
1696 /***********************************************************************
1697 * GetDriveTypeA (KERNEL32.@)
1699 UINT WINAPI
GetDriveTypeA( LPCSTR root
)
1701 UNICODE_STRING rootW
;
1706 if( !RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
1708 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1713 rootW
.Buffer
= NULL
;
1715 ret
= GetDriveTypeW(rootW
.Buffer
);
1717 RtlFreeUnicodeString(&rootW
);
1723 /***********************************************************************
1724 * GetCurrentDirectory (KERNEL.411)
1726 UINT16 WINAPI
GetCurrentDirectory16( UINT16 buflen
, LPSTR buf
)
1728 WCHAR cur_dirW
[MAX_PATH
];
1730 DRIVE_GetCurrentDirectory(MAX_PATH
, cur_dirW
);
1731 return (UINT16
)WideCharToMultiByte(CP_ACP
, 0, cur_dirW
, -1, buf
, buflen
, NULL
, NULL
);
1735 /***********************************************************************
1736 * GetCurrentDirectoryW (KERNEL32.@)
1738 UINT WINAPI
GetCurrentDirectoryW( UINT buflen
, LPWSTR buf
)
1741 WCHAR longname
[MAX_PATHNAME_LEN
];
1742 WCHAR shortname
[MAX_PATHNAME_LEN
];
1744 ret
= DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN
, shortname
);
1745 if ( ret
> MAX_PATHNAME_LEN
) {
1746 ERR_(file
)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret
);
1749 GetLongPathNameW(shortname
, longname
, MAX_PATHNAME_LEN
);
1750 ret
= strlenW( longname
) + 1;
1751 if (ret
> buflen
) return ret
;
1752 strcpyW(buf
, longname
);
1756 /***********************************************************************
1757 * GetCurrentDirectoryA (KERNEL32.@)
1759 UINT WINAPI
GetCurrentDirectoryA( UINT buflen
, LPSTR buf
)
1761 WCHAR bufferW
[MAX_PATH
];
1764 retW
= GetCurrentDirectoryW(MAX_PATH
, bufferW
);
1768 else if (retW
> MAX_PATH
)
1770 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1775 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
1778 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buf
, buflen
, NULL
, NULL
);
1779 ret
--; /* length without 0 */
1786 /***********************************************************************
1787 * SetCurrentDirectory (KERNEL.412)
1789 BOOL16 WINAPI
SetCurrentDirectory16( LPCSTR dir
)
1791 return SetCurrentDirectoryA( dir
);
1795 /***********************************************************************
1796 * SetCurrentDirectoryW (KERNEL32.@)
1798 BOOL WINAPI
SetCurrentDirectoryW( LPCWSTR dir
)
1800 int drive
, olddrive
= DRIVE_GetCurrentDrive();
1804 SetLastError(ERROR_INVALID_PARAMETER
);
1807 if (dir
[0] && (dir
[1]==':'))
1809 drive
= toupperW( *dir
) - 'A';
1815 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1816 sets pTask->curdir only if pTask->curdrive is drive */
1817 if (!(DRIVE_SetCurrentDrive( drive
)))
1820 /* FIXME: what about empty strings? Add a \\ ? */
1821 if (!DRIVE_Chdir( drive
, dir
)) {
1822 DRIVE_SetCurrentDrive(olddrive
);
1829 /***********************************************************************
1830 * SetCurrentDirectoryA (KERNEL32.@)
1832 BOOL WINAPI
SetCurrentDirectoryA( LPCSTR dir
)
1834 UNICODE_STRING dirW
;
1839 SetLastError(ERROR_INVALID_PARAMETER
);
1843 if (RtlCreateUnicodeStringFromAsciiz(&dirW
, dir
))
1845 ret
= SetCurrentDirectoryW(dirW
.Buffer
);
1846 RtlFreeUnicodeString(&dirW
);
1849 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1854 /***********************************************************************
1855 * GetLogicalDriveStringsA (KERNEL32.@)
1857 UINT WINAPI
GetLogicalDriveStringsA( UINT len
, LPSTR buffer
)
1861 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1862 if (DRIVE_IsValid(drive
)) count
++;
1863 if ((count
* 4) + 1 <= len
)
1866 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1867 if (DRIVE_IsValid(drive
))
1878 return (count
* 4) + 1; /* account for terminating null */
1879 /* The API tells about these different return values */
1883 /***********************************************************************
1884 * GetLogicalDriveStringsW (KERNEL32.@)
1886 UINT WINAPI
GetLogicalDriveStringsW( UINT len
, LPWSTR buffer
)
1890 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1891 if (DRIVE_IsValid(drive
)) count
++;
1892 if (count
* 4 * sizeof(WCHAR
) <= len
)
1895 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1896 if (DRIVE_IsValid(drive
))
1898 *p
++ = (WCHAR
)('a' + drive
);
1905 return count
* 4 * sizeof(WCHAR
);
1909 /***********************************************************************
1910 * GetLogicalDrives (KERNEL32.@)
1912 DWORD WINAPI
GetLogicalDrives(void)
1917 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1919 if ( (DRIVE_IsValid(drive
)) ||
1920 (DOSDrives
[drive
].type
== DRIVE_CDROM
)) /* audio CD is also valid */
1921 ret
|= (1 << drive
);
1927 /***********************************************************************
1928 * GetVolumeInformationW (KERNEL32.@)
1930 BOOL WINAPI
GetVolumeInformationW( LPCWSTR root
, LPWSTR label
,
1931 DWORD label_len
, DWORD
*serial
,
1932 DWORD
*filename_len
, DWORD
*flags
,
1933 LPWSTR fsname
, DWORD fsname_len
)
1938 /* FIXME, SetLastError()s missing */
1940 if (!root
) drive
= DRIVE_GetCurrentDrive();
1943 if (root
[0] && root
[1] != ':')
1945 WARN("invalid root %s\n", debugstr_w(root
));
1948 drive
= toupperW(root
[0]) - 'A';
1950 if (!DRIVE_IsValid( drive
)) return FALSE
;
1951 if (label
&& label_len
)
1953 strncpyW( label
, DRIVE_GetLabel(drive
), label_len
);
1954 label
[label_len
- 1] = 0; /* ensure 0 termination */
1955 cp
= label
+ strlenW(label
);
1956 while (cp
!= label
&& *(cp
-1) == ' ') cp
--;
1959 if (serial
) *serial
= DRIVE_GetSerialNumber(drive
);
1961 /* Set the filesystem information */
1962 /* Note: we only emulate a FAT fs at present */
1965 if (DOSDrives
[drive
].flags
& DRIVE_SHORT_NAMES
)
1968 *filename_len
= 255;
1973 if (DOSDrives
[drive
].flags
& DRIVE_CASE_SENSITIVE
)
1974 *flags
|=FS_CASE_SENSITIVE
;
1975 if (DOSDrives
[drive
].flags
& DRIVE_CASE_PRESERVING
)
1976 *flags
|=FS_CASE_IS_PRESERVED
;
1978 if (fsname
&& fsname_len
)
1980 /* Diablo checks that return code ... */
1981 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
1983 static const WCHAR cdfsW
[] = {'C','D','F','S',0};
1984 strncpyW( fsname
, cdfsW
, fsname_len
);
1988 static const WCHAR fatW
[] = {'F','A','T',0};
1989 strncpyW( fsname
, fatW
, fsname_len
);
1991 fsname
[fsname_len
- 1] = 0; /* ensure 0 termination */
1997 /***********************************************************************
1998 * GetVolumeInformationA (KERNEL32.@)
2000 BOOL WINAPI
GetVolumeInformationA( LPCSTR root
, LPSTR label
,
2001 DWORD label_len
, DWORD
*serial
,
2002 DWORD
*filename_len
, DWORD
*flags
,
2003 LPSTR fsname
, DWORD fsname_len
)
2005 UNICODE_STRING rootW
;
2006 LPWSTR labelW
, fsnameW
;
2009 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
2010 else rootW
.Buffer
= NULL
;
2011 labelW
= label
? HeapAlloc(GetProcessHeap(), 0, label_len
* sizeof(WCHAR
)) : NULL
;
2012 fsnameW
= fsname
? HeapAlloc(GetProcessHeap(), 0, fsname_len
* sizeof(WCHAR
)) : NULL
;
2014 if ((ret
= GetVolumeInformationW(rootW
.Buffer
, labelW
, label_len
, serial
,
2015 filename_len
, flags
, fsnameW
, fsname_len
)))
2017 if (label
) WideCharToMultiByte(CP_ACP
, 0, labelW
, -1, label
, label_len
, NULL
, NULL
);
2018 if (fsname
) WideCharToMultiByte(CP_ACP
, 0, fsnameW
, -1, fsname
, fsname_len
, NULL
, NULL
);
2021 RtlFreeUnicodeString(&rootW
);
2022 if (labelW
) HeapFree( GetProcessHeap(), 0, labelW
);
2023 if (fsnameW
) HeapFree( GetProcessHeap(), 0, fsnameW
);
2027 /***********************************************************************
2028 * SetVolumeLabelW (KERNEL32.@)
2030 BOOL WINAPI
SetVolumeLabelW( LPCWSTR root
, LPCWSTR volname
)
2034 /* FIXME, SetLastErrors missing */
2036 if (!root
) drive
= DRIVE_GetCurrentDrive();
2039 if ((root
[1]) && (root
[1] != ':'))
2041 WARN("invalid root %s\n", debugstr_w(root
));
2044 drive
= toupperW(root
[0]) - 'A';
2046 if (!DRIVE_IsValid( drive
)) return FALSE
;
2048 /* some copy protection stuff check this */
2049 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return FALSE
;
2051 strncpyW(DOSDrives
[drive
].label_conf
, volname
, 12);
2052 DOSDrives
[drive
].label_conf
[12 - 1] = 0; /* ensure 0 termination */
2056 /***********************************************************************
2057 * SetVolumeLabelA (KERNEL32.@)
2059 BOOL WINAPI
SetVolumeLabelA(LPCSTR root
, LPCSTR volname
)
2061 UNICODE_STRING rootW
, volnameW
;
2064 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
2065 else rootW
.Buffer
= NULL
;
2066 if (volname
) RtlCreateUnicodeStringFromAsciiz(&volnameW
, volname
);
2067 else volnameW
.Buffer
= NULL
;
2069 ret
= SetVolumeLabelW( rootW
.Buffer
, volnameW
.Buffer
);
2071 RtlFreeUnicodeString(&rootW
);
2072 RtlFreeUnicodeString(&volnameW
);
2076 /***********************************************************************
2077 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
2079 DWORD WINAPI
GetVolumeNameForVolumeMountPointW(LPWSTR str
, DWORD a
, DWORD b
)
2081 FIXME("(%s, %lx, %lx): stub\n", debugstr_w(str
), a
, b
);