Berend Reitsma <berend at asset-control.com>
[wine.git] / files / drive.c
blob2c69d0b9ee7806bc1919366b3d0bf6d7ec68f6f4
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 "windows.h"
36 #include "winbase.h"
37 #include "drive.h"
38 #include "file.h"
39 #include "heap.h"
40 #include "msdos.h"
41 #include "options.h"
42 #include "task.h"
43 #include "debug.h"
45 typedef struct
47 char *root; /* root dir in Unix format without trailing / */
48 char *dos_cwd; /* cwd in DOS format without leading or trailing \ */
49 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
50 char *device; /* raw device path */
51 char label[12]; /* drive label */
52 DWORD serial; /* drive serial number */
53 DRIVETYPE type; /* drive type */
54 UINT32 flags; /* drive flags */
55 dev_t dev; /* unix device number */
56 ino_t ino; /* unix inode number */
57 } DOSDRIVE;
60 static const char * const DRIVE_Types[] =
62 "floppy", /* TYPE_FLOPPY */
63 "hd", /* TYPE_HD */
64 "cdrom", /* TYPE_CDROM */
65 "network" /* TYPE_NETWORK */
69 /* Known filesystem types */
71 typedef struct
73 const char *name;
74 UINT32 flags;
75 } FS_DESCR;
77 static const FS_DESCR DRIVE_Filesystems[] =
79 { "unix", DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
80 { "msdos", DRIVE_SHORT_NAMES },
81 { "dos", DRIVE_SHORT_NAMES },
82 { "fat", DRIVE_SHORT_NAMES },
83 { "vfat", DRIVE_CASE_PRESERVING },
84 { "win95", DRIVE_CASE_PRESERVING },
85 { NULL, 0 }
89 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
90 static int DRIVE_CurDrive = -1;
92 static HTASK16 DRIVE_LastTask = 0;
95 /***********************************************************************
96 * DRIVE_GetDriveType
98 static DRIVETYPE DRIVE_GetDriveType( const char *name )
100 char buffer[20];
101 int i;
103 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
104 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
106 if (!strcasecmp( buffer, DRIVE_Types[i] )) return (DRIVETYPE)i;
108 MSG("%s: unknown type '%s', defaulting to 'hd'.\n", name, buffer );
109 return TYPE_HD;
113 /***********************************************************************
114 * DRIVE_GetFSFlags
116 static UINT32 DRIVE_GetFSFlags( const char *name, const char *value )
118 const FS_DESCR *descr;
120 for (descr = DRIVE_Filesystems; descr->name; descr++)
121 if (!strcasecmp( value, descr->name )) return descr->flags;
122 MSG("%s: unknown filesystem type '%s', defaulting to 'unix'.\n",
123 name, value );
124 return DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING;
128 /***********************************************************************
129 * DRIVE_Init
131 int DRIVE_Init(void)
133 int i, len, count = 0;
134 char name[] = "Drive A";
135 char path[MAX_PATHNAME_LEN];
136 char buffer[80];
137 struct stat drive_stat_buffer;
138 char *p;
139 DOSDRIVE *drive;
141 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
143 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
144 if (path[0])
146 p = path + strlen(path) - 1;
147 while ((p > path) && ((*p == '/') || (*p == '\\'))) *p-- = '\0';
148 if (!path[0]) strcpy( path, "/" );
150 if (stat( path, &drive_stat_buffer ))
152 MSG("Could not stat %s, ignoring drive %c:\n", path, 'A' + i );
153 continue;
155 if (!S_ISDIR(drive_stat_buffer.st_mode))
157 MSG("%s is not a directory, ignoring drive %c:\n",
158 path, 'A' + i );
159 continue;
162 drive->root = HEAP_strdupA( SystemHeap, 0, path );
163 drive->dos_cwd = HEAP_strdupA( SystemHeap, 0, "" );
164 drive->unix_cwd = HEAP_strdupA( SystemHeap, 0, "" );
165 drive->type = DRIVE_GetDriveType( name );
166 drive->device = NULL;
167 drive->flags = 0;
168 drive->dev = drive_stat_buffer.st_dev;
169 drive->ino = drive_stat_buffer.st_ino;
171 /* Get the drive label */
172 PROFILE_GetWineIniString( name, "Label", name, drive->label, 12 );
173 if ((len = strlen(drive->label)) < 11)
175 /* Pad label with spaces */
176 memset( drive->label + len, ' ', 11 - len );
177 drive->label[12] = '\0';
180 /* Get the serial number */
181 PROFILE_GetWineIniString( name, "Serial", "12345678",
182 buffer, sizeof(buffer) );
183 drive->serial = strtoul( buffer, NULL, 16 );
185 /* Get the filesystem type */
186 PROFILE_GetWineIniString( name, "Filesystem", "unix",
187 buffer, sizeof(buffer) );
188 drive->flags = DRIVE_GetFSFlags( name, buffer );
190 /* Get the device */
191 PROFILE_GetWineIniString( name, "Device", "",
192 buffer, sizeof(buffer) );
193 if (buffer[0])
194 drive->device = HEAP_strdupA( SystemHeap, 0, buffer );
196 /* Make the first hard disk the current drive */
197 if ((DRIVE_CurDrive == -1) && (drive->type == TYPE_HD))
198 DRIVE_CurDrive = i;
200 count++;
201 TRACE(dosfs, "%s: path=%s type=%s label='%s' serial=%08lx flags=%08x dev=%x ino=%x\n",
202 name, path, DRIVE_Types[drive->type],
203 drive->label, drive->serial, drive->flags,
204 (int)drive->dev, (int)drive->ino );
206 else WARN(dosfs, "%s: not defined\n", name );
209 if (!count)
211 MSG("Warning: no valid DOS drive found, check your configuration file.\n" );
212 /* Create a C drive pointing to Unix root dir */
213 DOSDrives[2].root = HEAP_strdupA( SystemHeap, 0, "/" );
214 DOSDrives[2].dos_cwd = HEAP_strdupA( SystemHeap, 0, "" );
215 DOSDrives[2].unix_cwd = HEAP_strdupA( SystemHeap, 0, "" );
216 strcpy( DOSDrives[2].label, "Drive C " );
217 DOSDrives[2].serial = 0x12345678;
218 DOSDrives[2].type = TYPE_HD;
219 DOSDrives[2].flags = 0;
220 DRIVE_CurDrive = 2;
223 /* Make sure the current drive is valid */
224 if (DRIVE_CurDrive == -1)
226 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
228 if (drive->root && !(drive->flags & DRIVE_DISABLED))
230 DRIVE_CurDrive = i;
231 break;
236 return 1;
240 /***********************************************************************
241 * DRIVE_IsValid
243 int DRIVE_IsValid( int drive )
245 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
246 return (DOSDrives[drive].root &&
247 !(DOSDrives[drive].flags & DRIVE_DISABLED));
251 /***********************************************************************
252 * DRIVE_GetCurrentDrive
254 int DRIVE_GetCurrentDrive(void)
256 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
257 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
258 return DRIVE_CurDrive;
262 /***********************************************************************
263 * DRIVE_SetCurrentDrive
265 int DRIVE_SetCurrentDrive( int drive )
267 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
268 if (!DRIVE_IsValid( drive ))
270 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
271 return 0;
273 TRACE(dosfs, "%c:\n", 'A' + drive );
274 DRIVE_CurDrive = drive;
275 if (pTask) pTask->curdrive = drive | 0x80;
276 return 1;
280 /***********************************************************************
281 * DRIVE_FindDriveRoot
283 * Find a drive for which the root matches the begginning of the given path.
284 * This can be used to translate a Unix path into a drive + DOS path.
285 * Return value is the drive, or -1 on error. On success, path is modified
286 * to point to the beginning of the DOS path.
288 int DRIVE_FindDriveRoot( const char **path )
290 /* idea: check at all '/' positions.
291 * If the device and inode of that path is identical with the
292 * device and inode of the current drive then we found a solution.
293 * If there is another drive pointing to a deeper position in
294 * the file tree, we want to find that one, not the earlier solution.
296 int drive, rootdrive = -1;
297 char buffer[MAX_PATHNAME_LEN];
298 char *next = buffer;
299 const char *p = *path;
300 struct stat st;
302 strcpy( buffer, "/" );
303 for (;;)
305 if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
307 /* Find the drive */
309 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
311 if (!DOSDrives[drive].root ||
312 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
314 if ((DOSDrives[drive].dev == st.st_dev) &&
315 (DOSDrives[drive].ino == st.st_ino))
317 rootdrive = drive;
318 *path = p;
322 /* Get the next path component */
324 *next++ = '/';
325 while ((*p == '/') || (*p == '\\')) p++;
326 if (!*p) break;
327 while (!IS_END_OF_NAME(*p)) *next++ = *p++;
328 *next = 0;
330 *next = 0;
332 if (rootdrive != -1)
333 TRACE(dosfs, "%s -> drive %c:, root='%s', name='%s'\n",
334 buffer, 'A' + rootdrive,
335 DOSDrives[rootdrive].root, *path );
336 return rootdrive;
340 /***********************************************************************
341 * DRIVE_GetRoot
343 const char * DRIVE_GetRoot( int drive )
345 if (!DRIVE_IsValid( drive )) return NULL;
346 return DOSDrives[drive].root;
350 /***********************************************************************
351 * DRIVE_GetDosCwd
353 const char * DRIVE_GetDosCwd( int drive )
355 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
356 if (!DRIVE_IsValid( drive )) return NULL;
358 /* Check if we need to change the directory to the new task. */
359 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
360 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
361 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
363 /* Perform the task-switch */
364 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
365 DRIVE_LastTask = GetCurrentTask();
367 return DOSDrives[drive].dos_cwd;
371 /***********************************************************************
372 * DRIVE_GetUnixCwd
374 const char * DRIVE_GetUnixCwd( int drive )
376 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
377 if (!DRIVE_IsValid( drive )) return NULL;
379 /* Check if we need to change the directory to the new task. */
380 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
381 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
382 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
384 /* Perform the task-switch */
385 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
386 DRIVE_LastTask = GetCurrentTask();
388 return DOSDrives[drive].unix_cwd;
392 /***********************************************************************
393 * DRIVE_GetLabel
395 const char * DRIVE_GetLabel( int drive )
397 if (!DRIVE_IsValid( drive )) return NULL;
398 return DOSDrives[drive].label;
402 /***********************************************************************
403 * DRIVE_GetSerialNumber
405 DWORD DRIVE_GetSerialNumber( int drive )
407 if (!DRIVE_IsValid( drive )) return 0;
408 return DOSDrives[drive].serial;
412 /***********************************************************************
413 * DRIVE_SetSerialNumber
415 int DRIVE_SetSerialNumber( int drive, DWORD serial )
417 if (!DRIVE_IsValid( drive )) return 0;
418 DOSDrives[drive].serial = serial;
419 return 1;
423 /***********************************************************************
424 * DRIVE_GetType
426 DRIVETYPE DRIVE_GetType( int drive )
428 if (!DRIVE_IsValid( drive )) return TYPE_INVALID;
429 return DOSDrives[drive].type;
433 /***********************************************************************
434 * DRIVE_GetFlags
436 UINT32 DRIVE_GetFlags( int drive )
438 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
439 return DOSDrives[drive].flags;
443 /***********************************************************************
444 * DRIVE_Chdir
446 int DRIVE_Chdir( int drive, const char *path )
448 DOS_FULL_NAME full_name;
449 char buffer[MAX_PATHNAME_LEN];
450 LPSTR unix_cwd;
451 BY_HANDLE_FILE_INFORMATION info;
452 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
454 strcpy( buffer, "A:" );
455 buffer[0] += drive;
456 TRACE(dosfs, "(%c:,%s)\n", buffer[0], path );
457 lstrcpyn32A( buffer + 2, path, sizeof(buffer) - 2 );
459 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
460 if (!FILE_Stat( full_name.long_name, &info )) return 0;
461 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
463 DOS_ERROR( ER_FileNotFound, EC_NotFound, SA_Abort, EL_Disk );
464 return 0;
466 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
467 while (*unix_cwd == '/') unix_cwd++;
469 TRACE(dosfs, "(%c:): unix_cwd=%s dos_cwd=%s\n",
470 'A' + drive, unix_cwd, full_name.short_name + 3 );
472 HeapFree( SystemHeap, 0, DOSDrives[drive].dos_cwd );
473 HeapFree( SystemHeap, 0, DOSDrives[drive].unix_cwd );
474 DOSDrives[drive].dos_cwd = HEAP_strdupA( SystemHeap, 0,
475 full_name.short_name + 3 );
476 DOSDrives[drive].unix_cwd = HEAP_strdupA( SystemHeap, 0, unix_cwd );
478 if (pTask && (pTask->curdrive & 0x80) &&
479 ((pTask->curdrive & ~0x80) == drive))
481 lstrcpyn32A( pTask->curdir, full_name.short_name + 2,
482 sizeof(pTask->curdir) );
483 DRIVE_LastTask = GetCurrentTask();
485 return 1;
489 /***********************************************************************
490 * DRIVE_Disable
492 int DRIVE_Disable( int drive )
494 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
496 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
497 return 0;
499 DOSDrives[drive].flags |= DRIVE_DISABLED;
500 return 1;
504 /***********************************************************************
505 * DRIVE_Enable
507 int DRIVE_Enable( int drive )
509 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
511 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
512 return 0;
514 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
515 return 1;
519 /***********************************************************************
520 * DRIVE_SetLogicalMapping
522 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
524 /* If new_drive is already valid, do nothing and return 0
525 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
527 DOSDRIVE *old, *new;
529 old = DOSDrives + existing_drive;
530 new = DOSDrives + new_drive;
532 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
533 !old->root ||
534 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
536 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
537 return 0;
540 if ( new->root )
542 TRACE(dosfs, "Can\'t map drive %c to drive %c - "
543 "drive %c already exists\n",
544 'A' + existing_drive, 'A' + new_drive,
545 'A' + new_drive );
546 /* it is already mapped there, so return success */
547 if (!strcmp(old->root,new->root))
548 return 1;
549 return 0;
552 new->root = HEAP_strdupA( SystemHeap, 0, old->root );
553 new->dos_cwd = HEAP_strdupA( SystemHeap, 0, old->dos_cwd );
554 new->unix_cwd = HEAP_strdupA( SystemHeap, 0, old->unix_cwd );
555 memcpy ( new->label, old->label, 12 );
556 new->serial = old->serial;
557 new->type = old->type;
558 new->flags = old->flags;
559 new->dev = old->dev;
560 new->ino = old->ino;
562 TRACE(dosfs, "Drive %c is now equal to drive %c\n",
563 'A' + new_drive, 'A' + existing_drive );
565 return 1;
569 /***********************************************************************
570 * DRIVE_OpenDevice
572 * Open the drive raw device and return a Unix fd (or -1 on error).
574 int DRIVE_OpenDevice( int drive, int flags )
576 if (!DRIVE_IsValid( drive )) return -1;
577 return open( DOSDrives[drive].device, flags );
581 /***********************************************************************
582 * DRIVE_RawRead
584 * Read raw sectors from a device
586 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL32 fake_success)
588 int fd;
590 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
592 lseek( fd, begin * 512, SEEK_SET );
593 /* FIXME: check errors */
594 read( fd, dataptr, nr_sect * 512 );
595 close( fd );
597 else
599 memset(dataptr, 0, nr_sect * 512);
600 if (fake_success)
602 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
603 if (begin == 1) *dataptr = 0xf8;
605 else
606 return 0;
608 return 1;
612 /***********************************************************************
613 * DRIVE_RawWrite
615 * Write raw sectors to a device
617 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL32 fake_success)
619 int fd;
621 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
623 lseek( fd, begin * 512, SEEK_SET );
624 /* FIXME: check errors */
625 write( fd, dataptr, nr_sect * 512 );
626 close( fd );
628 else
629 if (!(fake_success))
630 return 0;
632 return 1;
636 /***********************************************************************
637 * DRIVE_GetFreeSpace
639 static int DRIVE_GetFreeSpace( int drive, LPULARGE_INTEGER size,
640 LPULARGE_INTEGER available )
642 struct statfs info;
643 unsigned long long bigsize,bigavail=0;
645 if (!DRIVE_IsValid(drive))
647 DOS_ERROR( ER_InvalidDrive, EC_MediaError, SA_Abort, EL_Disk );
648 return 0;
651 /* FIXME: add autoconf check for this */
652 #if defined(__svr4__) || defined(_SCO_DS)
653 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
654 #else
655 if (statfs( DOSDrives[drive].root, &info) < 0)
656 #endif
658 FILE_SetDosError();
659 WARN(dosfs, "cannot do statfs(%s)\n", DOSDrives[drive].root);
660 return 0;
663 bigsize = (unsigned long long)info.f_bsize
664 * (unsigned long long)info.f_blocks;
665 #ifdef STATFS_HAS_BAVAIL
666 bigavail = (unsigned long long)info.f_bavail
667 * (unsigned long long)info.f_bsize;
668 #else
669 # ifdef STATFS_HAS_BFREE
670 bigavail = (unsigned long long)info.f_bfree
671 * (unsigned long long)info.f_bsize;
672 # else
673 # error "statfs has no bfree/bavail member!"
674 # endif
675 #endif
676 size->LowPart = (DWORD)bigsize;
677 size->HighPart = (DWORD)(bigsize>>32);
678 available->LowPart = (DWORD)bigavail;
679 available->HighPart = (DWORD)(bigavail>>32);
680 return 1;
684 /***********************************************************************
685 * GetDiskFreeSpace16 (KERNEL.422)
687 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
688 LPDWORD sector_bytes, LPDWORD free_clusters,
689 LPDWORD total_clusters )
691 return GetDiskFreeSpace32A( root, cluster_sectors, sector_bytes,
692 free_clusters, total_clusters );
696 /***********************************************************************
697 * GetDiskFreeSpace32A (KERNEL32.206)
699 * Fails if expression resulting from current drive's dir and "root"
700 * is not a root dir of the target drive.
702 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
703 * if the corresponding info is unneeded.
705 * FIXME: needs to support UNC names from Win95 OSR2 on.
707 * Behaviour under Win95a:
708 * CurrDir root result
709 * "E:\\TEST" "E:" FALSE
710 * "E:\\" "E:" TRUE
711 * "E:\\" "E" FALSE
712 * "E:\\" "\\" TRUE
713 * "E:\\TEST" "\\" TRUE
714 * "E:\\TEST" ":\\" FALSE
715 * "E:\\TEST" "E:\\" TRUE
716 * "E:\\TEST" "" FALSE
717 * "E:\\" "" FALSE (!)
718 * "E:\\" 0x0 TRUE
719 * "E:\\TEST" 0x0 TRUE (!)
720 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
721 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
723 BOOL32 WINAPI GetDiskFreeSpace32A( LPCSTR root, LPDWORD cluster_sectors,
724 LPDWORD sector_bytes, LPDWORD free_clusters,
725 LPDWORD total_clusters )
727 int drive;
728 ULARGE_INTEGER size,available;
729 LPCSTR path;
730 DWORD cluster_sec;
732 if ((!root) || (root == "\\"))
733 drive = DRIVE_GetCurrentDrive();
734 else
735 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
737 drive = toupper(root[0]) - 'A';
738 path = &root[2];
739 if (path[0] == '\0')
740 path = DRIVE_GetDosCwd(drive);
741 else
742 if (path[0] == '\\')
743 path++;
744 if (strlen(path)) /* oops, we are in a subdir */
745 return FALSE;
747 else
748 return FALSE;
750 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
752 /* Cap the size and available at 2GB as per specs. */
753 if ((size.HighPart) ||(size.LowPart > 0x7fffffff))
755 size.HighPart = 0;
756 size.LowPart = 0x7fffffff;
758 if ((available.HighPart) ||(available.LowPart > 0x7fffffff))
760 available.HighPart =0;
761 available.LowPart = 0x7fffffff;
763 if (DRIVE_GetType(drive)==TYPE_CDROM) {
764 if (sector_bytes)
765 *sector_bytes = 2048;
766 size.LowPart /= 2048;
767 available.LowPart /= 2048;
768 } else {
769 if (sector_bytes)
770 *sector_bytes = 512;
771 size.LowPart /= 512;
772 available.LowPart /= 512;
774 /* fixme: probably have to adjust those variables too for CDFS */
775 cluster_sec = 1;
776 while (cluster_sec * 65536 < size.LowPart) cluster_sec *= 2;
778 if (cluster_sectors)
779 *cluster_sectors = cluster_sec;
780 if (free_clusters)
781 *free_clusters = available.LowPart / cluster_sec;
782 if (total_clusters)
783 *total_clusters = size.LowPart / cluster_sec;
784 return TRUE;
788 /***********************************************************************
789 * GetDiskFreeSpace32W (KERNEL32.207)
791 BOOL32 WINAPI GetDiskFreeSpace32W( LPCWSTR root, LPDWORD cluster_sectors,
792 LPDWORD sector_bytes, LPDWORD free_clusters,
793 LPDWORD total_clusters )
795 LPSTR xroot;
796 BOOL32 ret;
798 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
799 ret = GetDiskFreeSpace32A( xroot,cluster_sectors, sector_bytes,
800 free_clusters, total_clusters );
801 HeapFree( GetProcessHeap(), 0, xroot );
802 return ret;
806 /***********************************************************************
807 * GetDiskFreeSpaceEx32A (KERNEL32.871)
809 BOOL32 WINAPI GetDiskFreeSpaceEx32A( LPCSTR root,
810 LPULARGE_INTEGER avail,
811 LPULARGE_INTEGER total,
812 LPULARGE_INTEGER totalfree)
814 int drive;
815 ULARGE_INTEGER size,available;
817 if (!root) drive = DRIVE_GetCurrentDrive();
818 else
820 if ((root[1]) && ((root[1] != ':') || (root[2] != '\\')))
822 WARN(dosfs, "invalid root '%s'\n", root );
823 return FALSE;
825 drive = toupper(root[0]) - 'A';
827 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
828 /*FIXME: Do we have the number of bytes available to the user? */
829 avail->HighPart = available.HighPart;
830 totalfree->HighPart = size.HighPart;
831 avail->LowPart = available.LowPart ;
832 totalfree->LowPart = size.LowPart ;
833 return TRUE;
836 /***********************************************************************
837 * GetDiskFreeSpaceEx32W (KERNEL32.873)
839 BOOL32 WINAPI GetDiskFreeSpaceEx32W( LPCWSTR root, LPULARGE_INTEGER avail,
840 LPULARGE_INTEGER total,
841 LPULARGE_INTEGER totalfree)
843 LPSTR xroot;
844 BOOL32 ret;
846 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
847 ret = GetDiskFreeSpaceEx32A( xroot, avail, total, totalfree);
848 HeapFree( GetProcessHeap(), 0, xroot );
849 return ret;
852 /***********************************************************************
853 * GetDriveType16 (KERNEL.136)
854 * This functions returns the drivetype of a drive in Win16.
855 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
856 * remote drive API. The returnvalue DRIVE_REMOTE for CD-ROMs has been
857 * verified on Win3.11 and Windows 95. Some programs rely on it, so don't
858 * do any pseudo-clever changes.
860 * RETURNS
861 * drivetype DRIVE_xxx
863 UINT16 WINAPI GetDriveType16(
864 UINT16 drive /* [in] number (NOT letter) of drive */
866 TRACE(dosfs, "(%c:)\n", 'A' + drive );
867 switch(DRIVE_GetType(drive))
869 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
870 case TYPE_HD: return DRIVE_FIXED;
871 case TYPE_CDROM: return DRIVE_REMOTE;
872 case TYPE_NETWORK: return DRIVE_REMOTE;
873 case TYPE_INVALID:
874 default: return DRIVE_CANNOTDETERMINE;
879 /***********************************************************************
880 * GetDriveType32A (KERNEL32.208)
882 * Returns the type of the disk drive specified. If root is NULL the
883 * root of the current directory is used.
885 * RETURNS
887 * Type of drive (from Win32 SDK):
889 * DRIVE_UNKNOWN unable to find out anything about the drive
890 * DRIVE_NO_ROOT_DIR nonexistand root dir
891 * DRIVE_REMOVABLE the disk can be removed from the machine
892 * DRIVE_FIXED the disk can not be removed from the machine
893 * DRIVE_REMOTE network disk
894 * DRIVE_CDROM CDROM drive
895 * DRIVE_RAMDISK virtual disk in ram
897 * DRIVE_DOESNOTEXIST XXX Not valid return value
898 * DRIVE_CANNOTDETERMINE XXX Not valid return value
900 * BUGS
902 * Currently returns DRIVE_DOESNOTEXIST and DRIVE_CANNOTDETERMINE
903 * when it really should return DRIVE_NO_ROOT_DIR and DRIVE_UNKNOWN.
904 * Why where the former defines used?
906 * DRIVE_RAMDISK is unsupported.
908 UINT32 WINAPI GetDriveType32A(LPCSTR root /* String describing drive */)
910 int drive;
911 TRACE(dosfs, "(%s)\n", debugstr_a(root));
913 if (NULL == root) drive = DRIVE_GetCurrentDrive();
914 else
916 if ((root[1]) && (root[1] != ':'))
918 WARN(dosfs, "invalid root '%s'\n", debugstr_a(root));
919 return DRIVE_DOESNOTEXIST;
921 drive = toupper(root[0]) - 'A';
923 switch(DRIVE_GetType(drive))
925 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
926 case TYPE_HD: return DRIVE_FIXED;
927 case TYPE_CDROM: return DRIVE_CDROM;
928 case TYPE_NETWORK: return DRIVE_REMOTE;
929 case TYPE_INVALID: return DRIVE_DOESNOTEXIST;
930 default: return DRIVE_CANNOTDETERMINE;
935 /***********************************************************************
936 * GetDriveType32W (KERNEL32.209)
938 UINT32 WINAPI GetDriveType32W( LPCWSTR root )
940 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
941 UINT32 ret = GetDriveType32A( xpath );
942 HeapFree( GetProcessHeap(), 0, xpath );
943 return ret;
947 /***********************************************************************
948 * GetCurrentDirectory16 (KERNEL.411)
950 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
952 return (UINT16)GetCurrentDirectory32A( buflen, buf );
956 /***********************************************************************
957 * GetCurrentDirectory32A (KERNEL32.196)
959 * Returns "X:\\path\\etc\\".
961 * Despite the API description, return required length including the
962 * terminating null when buffer too small. This is the real behaviour.
964 UINT32 WINAPI GetCurrentDirectory32A( UINT32 buflen, LPSTR buf )
966 UINT32 ret;
967 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
969 assert(s);
970 ret = strlen(s) + 3; /* length of WHOLE current directory */
971 if (ret >= buflen) return ret + 1;
972 lstrcpyn32A( buf, "A:\\", MIN( 4, buflen ) );
973 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
974 if (buflen > 3) lstrcpyn32A( buf + 3, s, buflen - 3 );
975 return ret;
979 /***********************************************************************
980 * GetCurrentDirectory32W (KERNEL32.197)
982 UINT32 WINAPI GetCurrentDirectory32W( UINT32 buflen, LPWSTR buf )
984 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
985 UINT32 ret = GetCurrentDirectory32A( buflen, xpath );
986 lstrcpyAtoW( buf, xpath );
987 HeapFree( GetProcessHeap(), 0, xpath );
988 return ret;
992 /***********************************************************************
993 * SetCurrentDirectory (KERNEL.412)
995 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
997 return SetCurrentDirectory32A( dir );
1001 /***********************************************************************
1002 * SetCurrentDirectory32A (KERNEL32.479)
1004 BOOL32 WINAPI SetCurrentDirectory32A( LPCSTR dir )
1006 int olddrive, drive = DRIVE_GetCurrentDrive();
1008 if (!dir) {
1009 ERR(file,"(NULL)!\n");
1010 return FALSE;
1012 if (dir[0] && (dir[1]==':'))
1014 drive = tolower( *dir ) - 'a';
1015 dir += 2;
1018 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1019 sets pTask->curdir only if pTask->curdrive is drive */
1020 olddrive = drive; /* in case DRIVE_Chdir fails */
1021 if (!(DRIVE_SetCurrentDrive( drive )))
1022 return FALSE;
1023 /* FIXME: what about empty strings? Add a \\ ? */
1024 if (!DRIVE_Chdir( drive, dir )) {
1025 DRIVE_SetCurrentDrive(olddrive);
1026 return FALSE;
1028 return TRUE;
1032 /***********************************************************************
1033 * SetCurrentDirectory32W (KERNEL32.480)
1035 BOOL32 WINAPI SetCurrentDirectory32W( LPCWSTR dirW )
1037 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1038 BOOL32 res = SetCurrentDirectory32A( dir );
1039 HeapFree( GetProcessHeap(), 0, dir );
1040 return res;
1044 /***********************************************************************
1045 * GetLogicalDriveStrings32A (KERNEL32.231)
1047 UINT32 WINAPI GetLogicalDriveStrings32A( UINT32 len, LPSTR buffer )
1049 int drive, count;
1051 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1052 if (DRIVE_IsValid(drive)) count++;
1053 if (count * 4 * sizeof(char) <= len)
1055 LPSTR p = buffer;
1056 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1057 if (DRIVE_IsValid(drive))
1059 *p++ = 'a' + drive;
1060 *p++ = ':';
1061 *p++ = '\\';
1062 *p++ = '\0';
1064 *p = '\0';
1066 return count * 4 * sizeof(char);
1070 /***********************************************************************
1071 * GetLogicalDriveStrings32W (KERNEL32.232)
1073 UINT32 WINAPI GetLogicalDriveStrings32W( UINT32 len, LPWSTR buffer )
1075 int drive, count;
1077 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1078 if (DRIVE_IsValid(drive)) count++;
1079 if (count * 4 * sizeof(WCHAR) <= len)
1081 LPWSTR p = buffer;
1082 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1083 if (DRIVE_IsValid(drive))
1085 *p++ = (WCHAR)('a' + drive);
1086 *p++ = (WCHAR)':';
1087 *p++ = (WCHAR)'\\';
1088 *p++ = (WCHAR)'\0';
1090 *p = (WCHAR)'\0';
1092 return count * 4 * sizeof(WCHAR);
1096 /***********************************************************************
1097 * GetLogicalDrives (KERNEL32.233)
1099 DWORD WINAPI GetLogicalDrives(void)
1101 DWORD ret = 0;
1102 int drive;
1104 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1105 if (DRIVE_IsValid(drive)) ret |= (1 << drive);
1106 return ret;
1110 /***********************************************************************
1111 * GetVolumeInformation32A (KERNEL32.309)
1113 BOOL32 WINAPI GetVolumeInformation32A( LPCSTR root, LPSTR label,
1114 DWORD label_len, DWORD *serial,
1115 DWORD *filename_len, DWORD *flags,
1116 LPSTR fsname, DWORD fsname_len )
1118 int drive;
1119 char *cp;
1121 /* FIXME, SetLastErrors missing */
1123 if (!root) drive = DRIVE_GetCurrentDrive();
1124 else
1126 if ((root[1]) && (root[1] != ':'))
1128 WARN(dosfs, "invalid root '%s'\n",root);
1129 return FALSE;
1131 drive = toupper(root[0]) - 'A';
1133 if (!DRIVE_IsValid( drive )) return FALSE;
1134 if (label)
1136 lstrcpyn32A( label, DRIVE_GetLabel(drive), label_len );
1137 for (cp = label; *cp; cp++);
1138 while (cp != label && *(cp-1) == ' ') cp--;
1139 *cp = '\0';
1141 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1143 /* Set the filesystem information */
1144 /* Note: we only emulate a FAT fs at the present */
1146 if (filename_len) {
1147 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1148 *filename_len = 12;
1149 else
1150 *filename_len = 255;
1152 if (flags) *flags = 0;
1153 if (fsname) {
1154 /* Diablo checks that return code ... */
1155 if (DRIVE_GetType(drive)==TYPE_CDROM)
1156 lstrcpyn32A( fsname, "CDFS", fsname_len );
1157 else
1158 lstrcpyn32A( fsname, "FAT", fsname_len );
1160 return TRUE;
1164 /***********************************************************************
1165 * GetVolumeInformation32W (KERNEL32.310)
1167 BOOL32 WINAPI GetVolumeInformation32W( LPCWSTR root, LPWSTR label,
1168 DWORD label_len, DWORD *serial,
1169 DWORD *filename_len, DWORD *flags,
1170 LPWSTR fsname, DWORD fsname_len )
1172 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1173 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1174 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1175 BOOL32 ret = GetVolumeInformation32A( xroot, xvolname, label_len, serial,
1176 filename_len, flags, xfsname,
1177 fsname_len );
1178 if (ret)
1180 if (label) lstrcpyAtoW( label, xvolname );
1181 if (fsname) lstrcpyAtoW( fsname, xfsname );
1183 HeapFree( GetProcessHeap(), 0, xroot );
1184 HeapFree( GetProcessHeap(), 0, xvolname );
1185 HeapFree( GetProcessHeap(), 0, xfsname );
1186 return ret;
1189 BOOL32 WINAPI SetVolumeLabel32A(LPCSTR rootpath,LPCSTR volname) {
1190 FIXME(dosfs,"(%s,%s),stub!\n",rootpath,volname);
1191 return TRUE;