Fix special case when no printer installed.
[wine/testsucceed.git] / files / drive.c
blobbd45c3f23af0367291b0cf5d9985dead5f586b0c
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 "debugtools.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 MESSAGE("%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 MESSAGE("%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 MESSAGE("Could not stat %s, ignoring drive %c:\n", path, 'A' + i );
158 continue;
160 if (!S_ISDIR(drive_stat_buffer.st_mode))
162 MESSAGE("%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 MESSAGE("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 * This function is used to aquire the size of the available and
815 * total space on a logical volume.
817 * RETURNS
819 * Zero on failure, nonzero upon success. Use GetLastError to obtain
820 * detailed error information.
823 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
824 PULARGE_INTEGER avail,
825 PULARGE_INTEGER total,
826 PULARGE_INTEGER totalfree)
828 int drive;
829 ULARGE_INTEGER size,available;
831 if (!root) drive = DRIVE_GetCurrentDrive();
832 else
834 if ((root[1]) && ((root[1] != ':') || (root[2] != '\\')))
836 FIXME_(dosfs)("there are valid root names which are not supported yet\n");
837 /* ..like UNC names, for instance. */
839 WARN_(dosfs)("invalid root '%s'\n", root );
840 return FALSE;
842 drive = toupper(root[0]) - 'A';
845 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
847 if (total)
849 total->HighPart = size.HighPart;
850 total->LowPart = size.LowPart ;
853 if (totalfree)
855 totalfree->HighPart = available.HighPart;
856 totalfree->LowPart = available.LowPart ;
859 if (avail)
861 if (FIXME_ON(dosfs))
863 /* On Windows2000, we need to check the disk quota
864 allocated for the user owning the calling process. We
865 don't want to be more obtrusive than necessary with the
866 FIXME messages, so don't print the FIXME unless Wine is
867 actually masquerading as Windows2000. */
869 OSVERSIONINFOA ovi;
870 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
871 if (GetVersionExA(&ovi))
873 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
874 FIXME_(dosfs)("no per-user quota support yet\n");
878 /* Quick hack, should eventually be fixed to work 100% with
879 Windows2000 (see comment above). */
880 avail->HighPart = totalfree->HighPart;
881 avail->LowPart = totalfree->LowPart ;
884 return TRUE;
887 /***********************************************************************
888 * GetDiskFreeSpaceEx32W (KERNEL32.873)
890 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
891 PULARGE_INTEGER total,
892 PULARGE_INTEGER totalfree)
894 LPSTR xroot;
895 BOOL ret;
897 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
898 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
899 HeapFree( GetProcessHeap(), 0, xroot );
900 return ret;
903 /***********************************************************************
904 * GetDriveType16 (KERNEL.136)
905 * This functions returns the drivetype of a drive in Win16.
906 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
907 * remote drive API. The returnvalue DRIVE_REMOTE for CD-ROMs has been
908 * verified on Win3.11 and Windows 95. Some programs rely on it, so don't
909 * do any pseudo-clever changes.
911 * RETURNS
912 * drivetype DRIVE_xxx
914 UINT16 WINAPI GetDriveType16(
915 UINT16 drive /* [in] number (NOT letter) of drive */
917 TRACE_(dosfs)("(%c:)\n", 'A' + drive );
918 switch(DRIVE_GetType(drive))
920 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
921 case TYPE_HD: return DRIVE_FIXED;
922 case TYPE_CDROM: return DRIVE_REMOTE;
923 case TYPE_NETWORK: return DRIVE_REMOTE;
924 case TYPE_INVALID:
925 default: return DRIVE_CANNOTDETERMINE;
930 /***********************************************************************
931 * GetDriveType32A (KERNEL32.208)
933 * Returns the type of the disk drive specified. If root is NULL the
934 * root of the current directory is used.
936 * RETURNS
938 * Type of drive (from Win32 SDK):
940 * DRIVE_UNKNOWN unable to find out anything about the drive
941 * DRIVE_NO_ROOT_DIR nonexistand root dir
942 * DRIVE_REMOVABLE the disk can be removed from the machine
943 * DRIVE_FIXED the disk can not be removed from the machine
944 * DRIVE_REMOTE network disk
945 * DRIVE_CDROM CDROM drive
946 * DRIVE_RAMDISK virtual disk in ram
948 * DRIVE_DOESNOTEXIST XXX Not valid return value
949 * DRIVE_CANNOTDETERMINE XXX Not valid return value
951 * BUGS
953 * Currently returns DRIVE_DOESNOTEXIST and DRIVE_CANNOTDETERMINE
954 * when it really should return DRIVE_NO_ROOT_DIR and DRIVE_UNKNOWN.
955 * Why where the former defines used?
957 * DRIVE_RAMDISK is unsupported.
959 UINT WINAPI GetDriveTypeA(LPCSTR root /* String describing drive */)
961 int drive;
962 TRACE_(dosfs)("(%s)\n", debugstr_a(root));
964 if (NULL == root) drive = DRIVE_GetCurrentDrive();
965 else
967 if ((root[1]) && (root[1] != ':'))
969 WARN_(dosfs)("invalid root '%s'\n", debugstr_a(root));
970 return DRIVE_DOESNOTEXIST;
972 drive = toupper(root[0]) - 'A';
974 switch(DRIVE_GetType(drive))
976 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
977 case TYPE_HD: return DRIVE_FIXED;
978 case TYPE_CDROM: return DRIVE_CDROM;
979 case TYPE_NETWORK: return DRIVE_REMOTE;
980 case TYPE_INVALID: return DRIVE_DOESNOTEXIST;
981 default: return DRIVE_CANNOTDETERMINE;
986 /***********************************************************************
987 * GetDriveType32W (KERNEL32.209)
989 UINT WINAPI GetDriveTypeW( LPCWSTR root )
991 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
992 UINT ret = GetDriveTypeA( xpath );
993 HeapFree( GetProcessHeap(), 0, xpath );
994 return ret;
998 /***********************************************************************
999 * GetCurrentDirectory16 (KERNEL.411)
1001 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1003 return (UINT16)GetCurrentDirectoryA( buflen, buf );
1007 /***********************************************************************
1008 * GetCurrentDirectory32A (KERNEL32.196)
1010 * Returns "X:\\path\\etc\\".
1012 * Despite the API description, return required length including the
1013 * terminating null when buffer too small. This is the real behaviour.
1015 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1017 UINT ret;
1018 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1020 assert(s);
1021 ret = strlen(s) + 3; /* length of WHOLE current directory */
1022 if (ret >= buflen) return ret + 1;
1023 lstrcpynA( buf, "A:\\", MIN( 4, buflen ) );
1024 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
1025 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
1026 return ret;
1030 /***********************************************************************
1031 * GetCurrentDirectory32W (KERNEL32.197)
1033 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1035 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1036 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1037 lstrcpyAtoW( buf, xpath );
1038 HeapFree( GetProcessHeap(), 0, xpath );
1039 return ret;
1043 /***********************************************************************
1044 * SetCurrentDirectory (KERNEL.412)
1046 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1048 return SetCurrentDirectoryA( dir );
1052 /***********************************************************************
1053 * SetCurrentDirectory32A (KERNEL32.479)
1055 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1057 int olddrive, drive = DRIVE_GetCurrentDrive();
1059 if (!dir) {
1060 ERR_(file)("(NULL)!\n");
1061 return FALSE;
1063 if (dir[0] && (dir[1]==':'))
1065 drive = tolower( *dir ) - 'a';
1066 dir += 2;
1069 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1070 sets pTask->curdir only if pTask->curdrive is drive */
1071 olddrive = drive; /* in case DRIVE_Chdir fails */
1072 if (!(DRIVE_SetCurrentDrive( drive )))
1073 return FALSE;
1074 /* FIXME: what about empty strings? Add a \\ ? */
1075 if (!DRIVE_Chdir( drive, dir )) {
1076 DRIVE_SetCurrentDrive(olddrive);
1077 return FALSE;
1079 return TRUE;
1083 /***********************************************************************
1084 * SetCurrentDirectory32W (KERNEL32.480)
1086 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1088 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1089 BOOL res = SetCurrentDirectoryA( dir );
1090 HeapFree( GetProcessHeap(), 0, dir );
1091 return res;
1095 /***********************************************************************
1096 * GetLogicalDriveStrings32A (KERNEL32.231)
1098 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1100 int drive, count;
1102 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1103 if (DRIVE_IsValid(drive)) count++;
1104 if (count * 4 * sizeof(char) <= len)
1106 LPSTR p = buffer;
1107 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1108 if (DRIVE_IsValid(drive))
1110 *p++ = 'a' + drive;
1111 *p++ = ':';
1112 *p++ = '\\';
1113 *p++ = '\0';
1115 *p = '\0';
1117 return count * 4 * sizeof(char);
1121 /***********************************************************************
1122 * GetLogicalDriveStrings32W (KERNEL32.232)
1124 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1126 int drive, count;
1128 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1129 if (DRIVE_IsValid(drive)) count++;
1130 if (count * 4 * sizeof(WCHAR) <= len)
1132 LPWSTR p = buffer;
1133 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1134 if (DRIVE_IsValid(drive))
1136 *p++ = (WCHAR)('a' + drive);
1137 *p++ = (WCHAR)':';
1138 *p++ = (WCHAR)'\\';
1139 *p++ = (WCHAR)'\0';
1141 *p = (WCHAR)'\0';
1143 return count * 4 * sizeof(WCHAR);
1147 /***********************************************************************
1148 * GetLogicalDrives (KERNEL32.233)
1150 DWORD WINAPI GetLogicalDrives(void)
1152 DWORD ret = 0;
1153 int drive;
1155 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1156 if (DRIVE_IsValid(drive)) ret |= (1 << drive);
1157 return ret;
1161 /***********************************************************************
1162 * GetVolumeInformation32A (KERNEL32.309)
1164 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1165 DWORD label_len, DWORD *serial,
1166 DWORD *filename_len, DWORD *flags,
1167 LPSTR fsname, DWORD fsname_len )
1169 int drive;
1170 char *cp;
1172 /* FIXME, SetLastErrors missing */
1174 if (!root) drive = DRIVE_GetCurrentDrive();
1175 else
1177 if ((root[1]) && (root[1] != ':'))
1179 WARN_(dosfs)("invalid root '%s'\n",root);
1180 return FALSE;
1182 drive = toupper(root[0]) - 'A';
1184 if (!DRIVE_IsValid( drive )) return FALSE;
1185 if (label)
1187 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1188 for (cp = label; *cp; cp++);
1189 while (cp != label && *(cp-1) == ' ') cp--;
1190 *cp = '\0';
1192 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1194 /* Set the filesystem information */
1195 /* Note: we only emulate a FAT fs at the present */
1197 if (filename_len) {
1198 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1199 *filename_len = 12;
1200 else
1201 *filename_len = 255;
1203 if (flags)
1205 *flags=0;
1206 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1207 *flags|=FS_CASE_SENSITIVE;
1208 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1209 *flags|=FS_CASE_IS_PRESERVED ;
1211 if (fsname) {
1212 /* Diablo checks that return code ... */
1213 if (DRIVE_GetType(drive)==TYPE_CDROM)
1214 lstrcpynA( fsname, "CDFS", fsname_len );
1215 else
1216 lstrcpynA( fsname, "FAT", fsname_len );
1218 return TRUE;
1222 /***********************************************************************
1223 * GetVolumeInformation32W (KERNEL32.310)
1225 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1226 DWORD label_len, DWORD *serial,
1227 DWORD *filename_len, DWORD *flags,
1228 LPWSTR fsname, DWORD fsname_len )
1230 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1231 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1232 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1233 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1234 filename_len, flags, xfsname,
1235 fsname_len );
1236 if (ret)
1238 if (label) lstrcpyAtoW( label, xvolname );
1239 if (fsname) lstrcpyAtoW( fsname, xfsname );
1241 HeapFree( GetProcessHeap(), 0, xroot );
1242 HeapFree( GetProcessHeap(), 0, xvolname );
1243 HeapFree( GetProcessHeap(), 0, xfsname );
1244 return ret;
1247 /***********************************************************************
1248 * SetVolumeLabelA (KERNEL32.675)
1250 BOOL WINAPI SetVolumeLabelA(LPCSTR rootpath,LPCSTR volname)
1252 FIXME_(dosfs)("(%s,%s),stub!\n",rootpath,volname);
1253 return TRUE;
1256 /***********************************************************************
1257 * SetVolumeLabelW (KERNEL32.676)
1259 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1261 LPSTR xroot, xvol;
1262 BOOL ret;
1264 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1265 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1266 ret = SetVolumeLabelA( xroot, xvol );
1267 HeapFree( GetProcessHeap(), 0, xroot );
1268 HeapFree( GetProcessHeap(), 0, xvol );
1269 return ret;