General tidy up of the MetaFile driver - make sure that everything
[wine.git] / files / drive.c
blob41418c8a5924c605306c0c35c6cfc2c0383e0985
1 /*
2 * DOS drives handling functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
6 */
8 #include "config.h"
10 #include <assert.h>
11 #include <ctype.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #include <errno.h>
18 #include <unistd.h>
20 #ifdef HAVE_SYS_PARAM_H
21 # include <sys/param.h>
22 #endif
23 #ifdef STATFS_DEFINED_BY_SYS_VFS
24 # include <sys/vfs.h>
25 #else
26 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
27 # include <sys/mount.h>
28 # else
29 # ifdef STATFS_DEFINED_BY_SYS_STATFS
30 # include <sys/statfs.h>
31 # endif
32 # endif
33 #endif
35 #include "winbase.h"
36 #include "wine/winbase16.h" /* for GetCurrentTask */
37 #include "wine/winestring.h" /* for lstrcpyAtoW */
38 #include "winerror.h"
39 #include "drive.h"
40 #include "file.h"
41 #include "heap.h"
42 #include "msdos.h"
43 #include "options.h"
44 #include "task.h"
45 #include "debug.h"
47 DECLARE_DEBUG_CHANNEL(dosfs)
48 DECLARE_DEBUG_CHANNEL(file)
50 typedef struct
52 char *root; /* root dir in Unix format without trailing / */
53 char *dos_cwd; /* cwd in DOS format without leading or trailing \ */
54 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
55 char *device; /* raw device path */
56 char label[12]; /* drive label */
57 DWORD serial; /* drive serial number */
58 DRIVETYPE type; /* drive type */
59 UINT flags; /* drive flags */
60 dev_t dev; /* unix device number */
61 ino_t ino; /* unix inode number */
62 } DOSDRIVE;
65 static const char * const DRIVE_Types[] =
67 "floppy", /* TYPE_FLOPPY */
68 "hd", /* TYPE_HD */
69 "cdrom", /* TYPE_CDROM */
70 "network" /* TYPE_NETWORK */
74 /* Known filesystem types */
76 typedef struct
78 const char *name;
79 UINT flags;
80 } FS_DESCR;
82 static const FS_DESCR DRIVE_Filesystems[] =
84 { "unix", DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
85 { "msdos", DRIVE_SHORT_NAMES },
86 { "dos", DRIVE_SHORT_NAMES },
87 { "fat", DRIVE_SHORT_NAMES },
88 { "vfat", DRIVE_CASE_PRESERVING },
89 { "win95", DRIVE_CASE_PRESERVING },
90 { NULL, 0 }
94 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
95 static int DRIVE_CurDrive = -1;
97 static HTASK16 DRIVE_LastTask = 0;
100 /***********************************************************************
101 * DRIVE_GetDriveType
103 static DRIVETYPE DRIVE_GetDriveType( const char *name )
105 char buffer[20];
106 int i;
108 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
109 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
111 if (!strcasecmp( buffer, DRIVE_Types[i] )) return (DRIVETYPE)i;
113 MSG("%s: unknown type '%s', defaulting to 'hd'.\n", name, buffer );
114 return TYPE_HD;
118 /***********************************************************************
119 * DRIVE_GetFSFlags
121 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
123 const FS_DESCR *descr;
125 for (descr = DRIVE_Filesystems; descr->name; descr++)
126 if (!strcasecmp( value, descr->name )) return descr->flags;
127 MSG("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
128 name, value );
129 return DRIVE_CASE_PRESERVING;
133 /***********************************************************************
134 * DRIVE_Init
136 int DRIVE_Init(void)
138 int i, len, count = 0;
139 char name[] = "Drive A";
140 char path[MAX_PATHNAME_LEN];
141 char buffer[80];
142 struct stat drive_stat_buffer;
143 char *p;
144 DOSDRIVE *drive;
146 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
148 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
149 if (path[0])
151 p = path + strlen(path) - 1;
152 while ((p > path) && ((*p == '/') || (*p == '\\'))) *p-- = '\0';
153 if (!path[0]) strcpy( path, "/" );
155 if (stat( path, &drive_stat_buffer ))
157 MSG("Could not stat %s, ignoring drive %c:\n", path, 'A' + i );
158 continue;
160 if (!S_ISDIR(drive_stat_buffer.st_mode))
162 MSG("%s is not a directory, ignoring drive %c:\n",
163 path, 'A' + i );
164 continue;
167 drive->root = HEAP_strdupA( SystemHeap, 0, path );
168 drive->dos_cwd = HEAP_strdupA( SystemHeap, 0, "" );
169 drive->unix_cwd = HEAP_strdupA( SystemHeap, 0, "" );
170 drive->type = DRIVE_GetDriveType( name );
171 drive->device = NULL;
172 drive->flags = 0;
173 drive->dev = drive_stat_buffer.st_dev;
174 drive->ino = drive_stat_buffer.st_ino;
176 /* Get the drive label */
177 PROFILE_GetWineIniString( name, "Label", name, drive->label, 12 );
178 if ((len = strlen(drive->label)) < 11)
180 /* Pad label with spaces */
181 memset( drive->label + len, ' ', 11 - len );
182 drive->label[12] = '\0';
185 /* Get the serial number */
186 PROFILE_GetWineIniString( name, "Serial", "12345678",
187 buffer, sizeof(buffer) );
188 drive->serial = strtoul( buffer, NULL, 16 );
190 /* Get the filesystem type */
191 PROFILE_GetWineIniString( name, "Filesystem", "win95",
192 buffer, sizeof(buffer) );
193 drive->flags = DRIVE_GetFSFlags( name, buffer );
195 /* Get the device */
196 PROFILE_GetWineIniString( name, "Device", "",
197 buffer, sizeof(buffer) );
198 if (buffer[0])
199 drive->device = HEAP_strdupA( SystemHeap, 0, buffer );
201 /* Make the first hard disk the current drive */
202 if ((DRIVE_CurDrive == -1) && (drive->type == TYPE_HD))
203 DRIVE_CurDrive = i;
205 count++;
206 TRACE(dosfs, "%s: path=%s type=%s label='%s' serial=%08lx flags=%08x dev=%x ino=%x\n",
207 name, path, DRIVE_Types[drive->type],
208 drive->label, drive->serial, drive->flags,
209 (int)drive->dev, (int)drive->ino );
211 else WARN(dosfs, "%s: not defined\n", name );
214 if (!count)
216 MSG("Warning: no valid DOS drive found, check your configuration file.\n" );
217 /* Create a C drive pointing to Unix root dir */
218 DOSDrives[2].root = HEAP_strdupA( SystemHeap, 0, "/" );
219 DOSDrives[2].dos_cwd = HEAP_strdupA( SystemHeap, 0, "" );
220 DOSDrives[2].unix_cwd = HEAP_strdupA( SystemHeap, 0, "" );
221 strcpy( DOSDrives[2].label, "Drive C " );
222 DOSDrives[2].serial = 0x12345678;
223 DOSDrives[2].type = TYPE_HD;
224 DOSDrives[2].flags = 0;
225 DRIVE_CurDrive = 2;
228 /* Make sure the current drive is valid */
229 if (DRIVE_CurDrive == -1)
231 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
233 if (drive->root && !(drive->flags & DRIVE_DISABLED))
235 DRIVE_CurDrive = i;
236 break;
241 return 1;
245 /***********************************************************************
246 * DRIVE_IsValid
248 int DRIVE_IsValid( int drive )
250 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
251 return (DOSDrives[drive].root &&
252 !(DOSDrives[drive].flags & DRIVE_DISABLED));
256 /***********************************************************************
257 * DRIVE_GetCurrentDrive
259 int DRIVE_GetCurrentDrive(void)
261 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
262 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
263 return DRIVE_CurDrive;
267 /***********************************************************************
268 * DRIVE_SetCurrentDrive
270 int DRIVE_SetCurrentDrive( int drive )
272 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
273 if (!DRIVE_IsValid( drive ))
275 SetLastError( ERROR_INVALID_DRIVE );
276 return 0;
278 TRACE(dosfs, "%c:\n", 'A' + drive );
279 DRIVE_CurDrive = drive;
280 if (pTask) pTask->curdrive = drive | 0x80;
281 return 1;
285 /***********************************************************************
286 * DRIVE_FindDriveRoot
288 * Find a drive for which the root matches the begginning of the given path.
289 * This can be used to translate a Unix path into a drive + DOS path.
290 * Return value is the drive, or -1 on error. On success, path is modified
291 * to point to the beginning of the DOS path.
293 int DRIVE_FindDriveRoot( const char **path )
295 /* idea: check at all '/' positions.
296 * If the device and inode of that path is identical with the
297 * device and inode of the current drive then we found a solution.
298 * If there is another drive pointing to a deeper position in
299 * the file tree, we want to find that one, not the earlier solution.
301 int drive, rootdrive = -1;
302 char buffer[MAX_PATHNAME_LEN];
303 char *next = buffer;
304 const char *p = *path;
305 struct stat st;
307 strcpy( buffer, "/" );
308 for (;;)
310 if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
312 /* Find the drive */
314 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
316 if (!DOSDrives[drive].root ||
317 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
319 if ((DOSDrives[drive].dev == st.st_dev) &&
320 (DOSDrives[drive].ino == st.st_ino))
322 rootdrive = drive;
323 *path = p;
327 /* Get the next path component */
329 *next++ = '/';
330 while ((*p == '/') || (*p == '\\')) p++;
331 if (!*p) break;
332 while (!IS_END_OF_NAME(*p)) *next++ = *p++;
333 *next = 0;
335 *next = 0;
337 if (rootdrive != -1)
338 TRACE(dosfs, "%s -> drive %c:, root='%s', name='%s'\n",
339 buffer, 'A' + rootdrive,
340 DOSDrives[rootdrive].root, *path );
341 return rootdrive;
345 /***********************************************************************
346 * DRIVE_GetRoot
348 const char * DRIVE_GetRoot( int drive )
350 if (!DRIVE_IsValid( drive )) return NULL;
351 return DOSDrives[drive].root;
355 /***********************************************************************
356 * DRIVE_GetDosCwd
358 const char * DRIVE_GetDosCwd( int drive )
360 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
361 if (!DRIVE_IsValid( drive )) return NULL;
363 /* Check if we need to change the directory to the new task. */
364 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
365 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
366 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
368 /* Perform the task-switch */
369 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
370 DRIVE_LastTask = GetCurrentTask();
372 return DOSDrives[drive].dos_cwd;
376 /***********************************************************************
377 * DRIVE_GetUnixCwd
379 const char * DRIVE_GetUnixCwd( int drive )
381 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
382 if (!DRIVE_IsValid( drive )) return NULL;
384 /* Check if we need to change the directory to the new task. */
385 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
386 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
387 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
389 /* Perform the task-switch */
390 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
391 DRIVE_LastTask = GetCurrentTask();
393 return DOSDrives[drive].unix_cwd;
397 /***********************************************************************
398 * DRIVE_GetLabel
400 const char * DRIVE_GetLabel( int drive )
402 if (!DRIVE_IsValid( drive )) return NULL;
403 return DOSDrives[drive].label;
407 /***********************************************************************
408 * DRIVE_GetSerialNumber
410 DWORD DRIVE_GetSerialNumber( int drive )
412 if (!DRIVE_IsValid( drive )) return 0;
413 return DOSDrives[drive].serial;
417 /***********************************************************************
418 * DRIVE_SetSerialNumber
420 int DRIVE_SetSerialNumber( int drive, DWORD serial )
422 if (!DRIVE_IsValid( drive )) return 0;
423 DOSDrives[drive].serial = serial;
424 return 1;
428 /***********************************************************************
429 * DRIVE_GetType
431 DRIVETYPE DRIVE_GetType( int drive )
433 if (!DRIVE_IsValid( drive )) return TYPE_INVALID;
434 return DOSDrives[drive].type;
438 /***********************************************************************
439 * DRIVE_GetFlags
441 UINT DRIVE_GetFlags( int drive )
443 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
444 return DOSDrives[drive].flags;
448 /***********************************************************************
449 * DRIVE_Chdir
451 int DRIVE_Chdir( int drive, const char *path )
453 DOS_FULL_NAME full_name;
454 char buffer[MAX_PATHNAME_LEN];
455 LPSTR unix_cwd;
456 BY_HANDLE_FILE_INFORMATION info;
457 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
459 strcpy( buffer, "A:" );
460 buffer[0] += drive;
461 TRACE(dosfs, "(%c:,%s)\n", buffer[0], path );
462 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
464 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
465 if (!FILE_Stat( full_name.long_name, &info )) return 0;
466 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
468 SetLastError( ERROR_FILE_NOT_FOUND );
469 return 0;
471 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
472 while (*unix_cwd == '/') unix_cwd++;
474 TRACE(dosfs, "(%c:): unix_cwd=%s dos_cwd=%s\n",
475 'A' + drive, unix_cwd, full_name.short_name + 3 );
477 HeapFree( SystemHeap, 0, DOSDrives[drive].dos_cwd );
478 HeapFree( SystemHeap, 0, DOSDrives[drive].unix_cwd );
479 DOSDrives[drive].dos_cwd = HEAP_strdupA( SystemHeap, 0,
480 full_name.short_name + 3 );
481 DOSDrives[drive].unix_cwd = HEAP_strdupA( SystemHeap, 0, unix_cwd );
483 if (pTask && (pTask->curdrive & 0x80) &&
484 ((pTask->curdrive & ~0x80) == drive))
486 lstrcpynA( pTask->curdir, full_name.short_name + 2,
487 sizeof(pTask->curdir) );
488 DRIVE_LastTask = GetCurrentTask();
490 return 1;
494 /***********************************************************************
495 * DRIVE_Disable
497 int DRIVE_Disable( int drive )
499 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
501 SetLastError( ERROR_INVALID_DRIVE );
502 return 0;
504 DOSDrives[drive].flags |= DRIVE_DISABLED;
505 return 1;
509 /***********************************************************************
510 * DRIVE_Enable
512 int DRIVE_Enable( int drive )
514 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
516 SetLastError( ERROR_INVALID_DRIVE );
517 return 0;
519 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
520 return 1;
524 /***********************************************************************
525 * DRIVE_SetLogicalMapping
527 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
529 /* If new_drive is already valid, do nothing and return 0
530 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
532 DOSDRIVE *old, *new;
534 old = DOSDrives + existing_drive;
535 new = DOSDrives + new_drive;
537 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
538 !old->root ||
539 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
541 SetLastError( ERROR_INVALID_DRIVE );
542 return 0;
545 if ( new->root )
547 TRACE(dosfs, "Can\'t map drive %c to drive %c - "
548 "drive %c already exists\n",
549 'A' + existing_drive, 'A' + new_drive,
550 'A' + new_drive );
551 /* it is already mapped there, so return success */
552 if (!strcmp(old->root,new->root))
553 return 1;
554 return 0;
557 new->root = HEAP_strdupA( SystemHeap, 0, old->root );
558 new->dos_cwd = HEAP_strdupA( SystemHeap, 0, old->dos_cwd );
559 new->unix_cwd = HEAP_strdupA( SystemHeap, 0, old->unix_cwd );
560 memcpy ( new->label, old->label, 12 );
561 new->serial = old->serial;
562 new->type = old->type;
563 new->flags = old->flags;
564 new->dev = old->dev;
565 new->ino = old->ino;
567 TRACE(dosfs, "Drive %c is now equal to drive %c\n",
568 'A' + new_drive, 'A' + existing_drive );
570 return 1;
574 /***********************************************************************
575 * DRIVE_OpenDevice
577 * Open the drive raw device and return a Unix fd (or -1 on error).
579 int DRIVE_OpenDevice( int drive, int flags )
581 if (!DRIVE_IsValid( drive )) return -1;
582 return open( DOSDrives[drive].device, flags );
586 /***********************************************************************
587 * DRIVE_RawRead
589 * Read raw sectors from a device
591 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
593 int fd;
595 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
597 lseek( fd, begin * 512, SEEK_SET );
598 /* FIXME: check errors */
599 read( fd, dataptr, nr_sect * 512 );
600 close( fd );
602 else
604 memset(dataptr, 0, nr_sect * 512);
605 if (fake_success)
607 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
608 if (begin == 1) *dataptr = 0xf8;
610 else
611 return 0;
613 return 1;
617 /***********************************************************************
618 * DRIVE_RawWrite
620 * Write raw sectors to a device
622 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
624 int fd;
626 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
628 lseek( fd, begin * 512, SEEK_SET );
629 /* FIXME: check errors */
630 write( fd, dataptr, nr_sect * 512 );
631 close( fd );
633 else
634 if (!(fake_success))
635 return 0;
637 return 1;
641 /***********************************************************************
642 * DRIVE_GetFreeSpace
644 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
645 PULARGE_INTEGER available )
647 struct statfs info;
648 unsigned long long bigsize,bigavail=0;
650 if (!DRIVE_IsValid(drive))
652 SetLastError( ERROR_INVALID_DRIVE );
653 return 0;
656 /* FIXME: add autoconf check for this */
657 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
658 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
659 #else
660 if (statfs( DOSDrives[drive].root, &info) < 0)
661 #endif
663 FILE_SetDosError();
664 WARN(dosfs, "cannot do statfs(%s)\n", DOSDrives[drive].root);
665 return 0;
668 bigsize = (unsigned long long)info.f_bsize
669 * (unsigned long long)info.f_blocks;
670 #ifdef STATFS_HAS_BAVAIL
671 bigavail = (unsigned long long)info.f_bavail
672 * (unsigned long long)info.f_bsize;
673 #else
674 # ifdef STATFS_HAS_BFREE
675 bigavail = (unsigned long long)info.f_bfree
676 * (unsigned long long)info.f_bsize;
677 # else
678 # error "statfs has no bfree/bavail member!"
679 # endif
680 #endif
681 size->LowPart = (DWORD)bigsize;
682 size->HighPart = (DWORD)(bigsize>>32);
683 available->LowPart = (DWORD)bigavail;
684 available->HighPart = (DWORD)(bigavail>>32);
685 return 1;
689 /***********************************************************************
690 * GetDiskFreeSpace16 (KERNEL.422)
692 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
693 LPDWORD sector_bytes, LPDWORD free_clusters,
694 LPDWORD total_clusters )
696 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
697 free_clusters, total_clusters );
701 /***********************************************************************
702 * GetDiskFreeSpace32A (KERNEL32.206)
704 * Fails if expression resulting from current drive's dir and "root"
705 * is not a root dir of the target drive.
707 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
708 * if the corresponding info is unneeded.
710 * FIXME: needs to support UNC names from Win95 OSR2 on.
712 * Behaviour under Win95a:
713 * CurrDir root result
714 * "E:\\TEST" "E:" FALSE
715 * "E:\\" "E:" TRUE
716 * "E:\\" "E" FALSE
717 * "E:\\" "\\" TRUE
718 * "E:\\TEST" "\\" TRUE
719 * "E:\\TEST" ":\\" FALSE
720 * "E:\\TEST" "E:\\" TRUE
721 * "E:\\TEST" "" FALSE
722 * "E:\\" "" FALSE (!)
723 * "E:\\" 0x0 TRUE
724 * "E:\\TEST" 0x0 TRUE (!)
725 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
726 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
728 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
729 LPDWORD sector_bytes, LPDWORD free_clusters,
730 LPDWORD total_clusters )
732 int drive;
733 ULARGE_INTEGER size,available;
734 LPCSTR path;
735 DWORD cluster_sec;
737 if ((!root) || (root == "\\"))
738 drive = DRIVE_GetCurrentDrive();
739 else
740 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
742 drive = toupper(root[0]) - 'A';
743 path = &root[2];
744 if (path[0] == '\0')
745 path = DRIVE_GetDosCwd(drive);
746 else
747 if (path[0] == '\\')
748 path++;
749 if (strlen(path)) /* oops, we are in a subdir */
750 return FALSE;
752 else
753 return FALSE;
755 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
757 /* Cap the size and available at 2GB as per specs. */
758 if ((size.HighPart) ||(size.LowPart > 0x7fffffff))
760 size.HighPart = 0;
761 size.LowPart = 0x7fffffff;
763 if ((available.HighPart) ||(available.LowPart > 0x7fffffff))
765 available.HighPart =0;
766 available.LowPart = 0x7fffffff;
768 if (DRIVE_GetType(drive)==TYPE_CDROM) {
769 if (sector_bytes)
770 *sector_bytes = 2048;
771 size.LowPart /= 2048;
772 available.LowPart /= 2048;
773 } else {
774 if (sector_bytes)
775 *sector_bytes = 512;
776 size.LowPart /= 512;
777 available.LowPart /= 512;
779 /* fixme: probably have to adjust those variables too for CDFS */
780 cluster_sec = 1;
781 while (cluster_sec * 65536 < size.LowPart) cluster_sec *= 2;
783 if (cluster_sectors)
784 *cluster_sectors = cluster_sec;
785 if (free_clusters)
786 *free_clusters = available.LowPart / cluster_sec;
787 if (total_clusters)
788 *total_clusters = size.LowPart / cluster_sec;
789 return TRUE;
793 /***********************************************************************
794 * GetDiskFreeSpace32W (KERNEL32.207)
796 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
797 LPDWORD sector_bytes, LPDWORD free_clusters,
798 LPDWORD total_clusters )
800 LPSTR xroot;
801 BOOL ret;
803 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
804 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
805 free_clusters, total_clusters );
806 HeapFree( GetProcessHeap(), 0, xroot );
807 return ret;
811 /***********************************************************************
812 * GetDiskFreeSpaceEx32A (KERNEL32.871)
814 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
815 PULARGE_INTEGER avail,
816 PULARGE_INTEGER total,
817 PULARGE_INTEGER totalfree)
819 int drive;
820 ULARGE_INTEGER size,available;
822 if (!root) drive = DRIVE_GetCurrentDrive();
823 else
825 if ((root[1]) && ((root[1] != ':') || (root[2] != '\\')))
827 WARN(dosfs, "invalid root '%s'\n", root );
828 return FALSE;
830 drive = toupper(root[0]) - 'A';
832 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
833 /*FIXME: Do we have the number of bytes available to the user? */
834 if (totalfree) {
835 totalfree->HighPart = size.HighPart;
836 totalfree->LowPart = size.LowPart ;
838 if (avail) {
839 avail->HighPart = available.HighPart;
840 avail->LowPart = available.LowPart ;
842 return TRUE;
845 /***********************************************************************
846 * GetDiskFreeSpaceEx32W (KERNEL32.873)
848 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
849 PULARGE_INTEGER total,
850 PULARGE_INTEGER totalfree)
852 LPSTR xroot;
853 BOOL ret;
855 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
856 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
857 HeapFree( GetProcessHeap(), 0, xroot );
858 return ret;
861 /***********************************************************************
862 * GetDriveType16 (KERNEL.136)
863 * This functions returns the drivetype of a drive in Win16.
864 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
865 * remote drive API. The returnvalue DRIVE_REMOTE for CD-ROMs has been
866 * verified on Win3.11 and Windows 95. Some programs rely on it, so don't
867 * do any pseudo-clever changes.
869 * RETURNS
870 * drivetype DRIVE_xxx
872 UINT16 WINAPI GetDriveType16(
873 UINT16 drive /* [in] number (NOT letter) of drive */
875 TRACE(dosfs, "(%c:)\n", 'A' + drive );
876 switch(DRIVE_GetType(drive))
878 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
879 case TYPE_HD: return DRIVE_FIXED;
880 case TYPE_CDROM: return DRIVE_REMOTE;
881 case TYPE_NETWORK: return DRIVE_REMOTE;
882 case TYPE_INVALID:
883 default: return DRIVE_CANNOTDETERMINE;
888 /***********************************************************************
889 * GetDriveType32A (KERNEL32.208)
891 * Returns the type of the disk drive specified. If root is NULL the
892 * root of the current directory is used.
894 * RETURNS
896 * Type of drive (from Win32 SDK):
898 * DRIVE_UNKNOWN unable to find out anything about the drive
899 * DRIVE_NO_ROOT_DIR nonexistand root dir
900 * DRIVE_REMOVABLE the disk can be removed from the machine
901 * DRIVE_FIXED the disk can not be removed from the machine
902 * DRIVE_REMOTE network disk
903 * DRIVE_CDROM CDROM drive
904 * DRIVE_RAMDISK virtual disk in ram
906 * DRIVE_DOESNOTEXIST XXX Not valid return value
907 * DRIVE_CANNOTDETERMINE XXX Not valid return value
909 * BUGS
911 * Currently returns DRIVE_DOESNOTEXIST and DRIVE_CANNOTDETERMINE
912 * when it really should return DRIVE_NO_ROOT_DIR and DRIVE_UNKNOWN.
913 * Why where the former defines used?
915 * DRIVE_RAMDISK is unsupported.
917 UINT WINAPI GetDriveTypeA(LPCSTR root /* String describing drive */)
919 int drive;
920 TRACE(dosfs, "(%s)\n", debugstr_a(root));
922 if (NULL == root) drive = DRIVE_GetCurrentDrive();
923 else
925 if ((root[1]) && (root[1] != ':'))
927 WARN(dosfs, "invalid root '%s'\n", debugstr_a(root));
928 return DRIVE_DOESNOTEXIST;
930 drive = toupper(root[0]) - 'A';
932 switch(DRIVE_GetType(drive))
934 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
935 case TYPE_HD: return DRIVE_FIXED;
936 case TYPE_CDROM: return DRIVE_CDROM;
937 case TYPE_NETWORK: return DRIVE_REMOTE;
938 case TYPE_INVALID: return DRIVE_DOESNOTEXIST;
939 default: return DRIVE_CANNOTDETERMINE;
944 /***********************************************************************
945 * GetDriveType32W (KERNEL32.209)
947 UINT WINAPI GetDriveTypeW( LPCWSTR root )
949 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
950 UINT ret = GetDriveTypeA( xpath );
951 HeapFree( GetProcessHeap(), 0, xpath );
952 return ret;
956 /***********************************************************************
957 * GetCurrentDirectory16 (KERNEL.411)
959 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
961 return (UINT16)GetCurrentDirectoryA( buflen, buf );
965 /***********************************************************************
966 * GetCurrentDirectory32A (KERNEL32.196)
968 * Returns "X:\\path\\etc\\".
970 * Despite the API description, return required length including the
971 * terminating null when buffer too small. This is the real behaviour.
973 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
975 UINT ret;
976 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
978 assert(s);
979 ret = strlen(s) + 3; /* length of WHOLE current directory */
980 if (ret >= buflen) return ret + 1;
981 lstrcpynA( buf, "A:\\", MIN( 4, buflen ) );
982 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
983 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
984 return ret;
988 /***********************************************************************
989 * GetCurrentDirectory32W (KERNEL32.197)
991 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
993 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
994 UINT ret = GetCurrentDirectoryA( buflen, xpath );
995 lstrcpyAtoW( buf, xpath );
996 HeapFree( GetProcessHeap(), 0, xpath );
997 return ret;
1001 /***********************************************************************
1002 * SetCurrentDirectory (KERNEL.412)
1004 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1006 return SetCurrentDirectoryA( dir );
1010 /***********************************************************************
1011 * SetCurrentDirectory32A (KERNEL32.479)
1013 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1015 int olddrive, drive = DRIVE_GetCurrentDrive();
1017 if (!dir) {
1018 ERR(file,"(NULL)!\n");
1019 return FALSE;
1021 if (dir[0] && (dir[1]==':'))
1023 drive = tolower( *dir ) - 'a';
1024 dir += 2;
1027 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1028 sets pTask->curdir only if pTask->curdrive is drive */
1029 olddrive = drive; /* in case DRIVE_Chdir fails */
1030 if (!(DRIVE_SetCurrentDrive( drive )))
1031 return FALSE;
1032 /* FIXME: what about empty strings? Add a \\ ? */
1033 if (!DRIVE_Chdir( drive, dir )) {
1034 DRIVE_SetCurrentDrive(olddrive);
1035 return FALSE;
1037 return TRUE;
1041 /***********************************************************************
1042 * SetCurrentDirectory32W (KERNEL32.480)
1044 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1046 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1047 BOOL res = SetCurrentDirectoryA( dir );
1048 HeapFree( GetProcessHeap(), 0, dir );
1049 return res;
1053 /***********************************************************************
1054 * GetLogicalDriveStrings32A (KERNEL32.231)
1056 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1058 int drive, count;
1060 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1061 if (DRIVE_IsValid(drive)) count++;
1062 if (count * 4 * sizeof(char) <= len)
1064 LPSTR p = buffer;
1065 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1066 if (DRIVE_IsValid(drive))
1068 *p++ = 'a' + drive;
1069 *p++ = ':';
1070 *p++ = '\\';
1071 *p++ = '\0';
1073 *p = '\0';
1075 return count * 4 * sizeof(char);
1079 /***********************************************************************
1080 * GetLogicalDriveStrings32W (KERNEL32.232)
1082 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1084 int drive, count;
1086 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1087 if (DRIVE_IsValid(drive)) count++;
1088 if (count * 4 * sizeof(WCHAR) <= len)
1090 LPWSTR p = buffer;
1091 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1092 if (DRIVE_IsValid(drive))
1094 *p++ = (WCHAR)('a' + drive);
1095 *p++ = (WCHAR)':';
1096 *p++ = (WCHAR)'\\';
1097 *p++ = (WCHAR)'\0';
1099 *p = (WCHAR)'\0';
1101 return count * 4 * sizeof(WCHAR);
1105 /***********************************************************************
1106 * GetLogicalDrives (KERNEL32.233)
1108 DWORD WINAPI GetLogicalDrives(void)
1110 DWORD ret = 0;
1111 int drive;
1113 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1114 if (DRIVE_IsValid(drive)) ret |= (1 << drive);
1115 return ret;
1119 /***********************************************************************
1120 * GetVolumeInformation32A (KERNEL32.309)
1122 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1123 DWORD label_len, DWORD *serial,
1124 DWORD *filename_len, DWORD *flags,
1125 LPSTR fsname, DWORD fsname_len )
1127 int drive;
1128 char *cp;
1130 /* FIXME, SetLastErrors missing */
1132 if (!root) drive = DRIVE_GetCurrentDrive();
1133 else
1135 if ((root[1]) && (root[1] != ':'))
1137 WARN(dosfs, "invalid root '%s'\n",root);
1138 return FALSE;
1140 drive = toupper(root[0]) - 'A';
1142 if (!DRIVE_IsValid( drive )) return FALSE;
1143 if (label)
1145 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1146 for (cp = label; *cp; cp++);
1147 while (cp != label && *(cp-1) == ' ') cp--;
1148 *cp = '\0';
1150 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1152 /* Set the filesystem information */
1153 /* Note: we only emulate a FAT fs at the present */
1155 if (filename_len) {
1156 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1157 *filename_len = 12;
1158 else
1159 *filename_len = 255;
1161 if (flags)
1163 *flags=0;
1164 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1165 *flags|=FS_CASE_SENSITIVE;
1166 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1167 *flags|=FS_CASE_IS_PRESERVED ;
1169 if (fsname) {
1170 /* Diablo checks that return code ... */
1171 if (DRIVE_GetType(drive)==TYPE_CDROM)
1172 lstrcpynA( fsname, "CDFS", fsname_len );
1173 else
1174 lstrcpynA( fsname, "FAT", fsname_len );
1176 return TRUE;
1180 /***********************************************************************
1181 * GetVolumeInformation32W (KERNEL32.310)
1183 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1184 DWORD label_len, DWORD *serial,
1185 DWORD *filename_len, DWORD *flags,
1186 LPWSTR fsname, DWORD fsname_len )
1188 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1189 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1190 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1191 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1192 filename_len, flags, xfsname,
1193 fsname_len );
1194 if (ret)
1196 if (label) lstrcpyAtoW( label, xvolname );
1197 if (fsname) lstrcpyAtoW( fsname, xfsname );
1199 HeapFree( GetProcessHeap(), 0, xroot );
1200 HeapFree( GetProcessHeap(), 0, xvolname );
1201 HeapFree( GetProcessHeap(), 0, xfsname );
1202 return ret;
1205 BOOL WINAPI SetVolumeLabelA(LPCSTR rootpath,LPCSTR volname) {
1206 FIXME(dosfs,"(%s,%s),stub!\n",rootpath,volname);
1207 return TRUE;