Returns an error if trying to write to a stream opened for read.
[wine.git] / files / drive.c
blob7363c2f7333f5262b4c0b6ab5550f745fcdb1f1e
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 "wine/port.h"
45 #include "task.h"
46 #include "debugtools.h"
48 DEFAULT_DEBUG_CHANNEL(dosfs)
49 DECLARE_DEBUG_CHANNEL(file)
51 typedef struct
53 char *root; /* root dir in Unix format without trailing / */
54 char *dos_cwd; /* cwd in DOS format without leading or trailing \ */
55 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
56 char *device; /* raw device path */
57 char label[12]; /* drive label */
58 DWORD serial; /* drive serial number */
59 DRIVETYPE type; /* drive type */
60 UINT flags; /* drive flags */
61 dev_t dev; /* unix device number */
62 ino_t ino; /* unix inode number */
63 } DOSDRIVE;
66 static const char * const DRIVE_Types[] =
68 "floppy", /* TYPE_FLOPPY */
69 "hd", /* TYPE_HD */
70 "cdrom", /* TYPE_CDROM */
71 "network" /* TYPE_NETWORK */
75 /* Known filesystem types */
77 typedef struct
79 const char *name;
80 UINT flags;
81 } FS_DESCR;
83 static const FS_DESCR DRIVE_Filesystems[] =
85 { "unix", DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
86 { "msdos", DRIVE_SHORT_NAMES },
87 { "dos", DRIVE_SHORT_NAMES },
88 { "fat", DRIVE_SHORT_NAMES },
89 { "vfat", DRIVE_CASE_PRESERVING },
90 { "win95", DRIVE_CASE_PRESERVING },
91 { NULL, 0 }
95 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
96 static int DRIVE_CurDrive = -1;
98 static HTASK16 DRIVE_LastTask = 0;
101 /***********************************************************************
102 * DRIVE_GetDriveType
104 static DRIVETYPE DRIVE_GetDriveType( const char *name )
106 char buffer[20];
107 int i;
109 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
110 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
112 if (!strcasecmp( buffer, DRIVE_Types[i] )) return (DRIVETYPE)i;
114 MESSAGE("%s: unknown type '%s', defaulting to 'hd'.\n", name, buffer );
115 return TYPE_HD;
119 /***********************************************************************
120 * DRIVE_GetFSFlags
122 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
124 const FS_DESCR *descr;
126 for (descr = DRIVE_Filesystems; descr->name; descr++)
127 if (!strcasecmp( value, descr->name )) return descr->flags;
128 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
129 name, value );
130 return DRIVE_CASE_PRESERVING;
134 /***********************************************************************
135 * DRIVE_Init
137 int DRIVE_Init(void)
139 int i, len, count = 0;
140 char name[] = "Drive A";
141 char path[MAX_PATHNAME_LEN];
142 char buffer[80];
143 struct stat drive_stat_buffer;
144 char *p;
145 DOSDRIVE *drive;
147 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
149 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
150 if (path[0])
152 p = path + strlen(path) - 1;
153 while ((p > path) && ((*p == '/') || (*p == '\\'))) *p-- = '\0';
154 if (!path[0]) strcpy( path, "/" );
156 if (stat( path, &drive_stat_buffer ))
158 MESSAGE("Could not stat %s, ignoring drive %c:\n", path, 'A' + i );
159 continue;
161 if (!S_ISDIR(drive_stat_buffer.st_mode))
163 MESSAGE("%s is not a directory, ignoring drive %c:\n",
164 path, 'A' + i );
165 continue;
168 drive->root = HEAP_strdupA( SystemHeap, 0, path );
169 drive->dos_cwd = HEAP_strdupA( SystemHeap, 0, "" );
170 drive->unix_cwd = HEAP_strdupA( SystemHeap, 0, "" );
171 drive->type = DRIVE_GetDriveType( name );
172 drive->device = NULL;
173 drive->flags = 0;
174 drive->dev = drive_stat_buffer.st_dev;
175 drive->ino = drive_stat_buffer.st_ino;
177 /* Get the drive label */
178 PROFILE_GetWineIniString( name, "Label", name, drive->label, 12 );
179 if ((len = strlen(drive->label)) < 11)
181 /* Pad label with spaces */
182 memset( drive->label + len, ' ', 11 - len );
183 drive->label[12] = '\0';
186 /* Get the serial number */
187 PROFILE_GetWineIniString( name, "Serial", "12345678",
188 buffer, sizeof(buffer) );
189 drive->serial = strtoul( buffer, NULL, 16 );
191 /* Get the filesystem type */
192 PROFILE_GetWineIniString( name, "Filesystem", "win95",
193 buffer, sizeof(buffer) );
194 drive->flags = DRIVE_GetFSFlags( name, buffer );
196 /* Get the device */
197 PROFILE_GetWineIniString( name, "Device", "",
198 buffer, sizeof(buffer) );
199 if (buffer[0])
200 drive->device = HEAP_strdupA( SystemHeap, 0, buffer );
202 /* Make the first hard disk the current drive */
203 if ((DRIVE_CurDrive == -1) && (drive->type == TYPE_HD))
204 DRIVE_CurDrive = i;
206 count++;
207 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
208 "flags=%08x dev=%x ino=%x\n",
209 name, path, DRIVE_Types[drive->type],
210 drive->label, drive->serial, drive->flags,
211 (int)drive->dev, (int)drive->ino );
213 else WARN("%s: not defined\n", name );
216 if (!count)
218 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
219 /* Create a C drive pointing to Unix root dir */
220 DOSDrives[2].root = HEAP_strdupA( SystemHeap, 0, "/" );
221 DOSDrives[2].dos_cwd = HEAP_strdupA( SystemHeap, 0, "" );
222 DOSDrives[2].unix_cwd = HEAP_strdupA( SystemHeap, 0, "" );
223 strcpy( DOSDrives[2].label, "Drive C " );
224 DOSDrives[2].serial = 0x12345678;
225 DOSDrives[2].type = TYPE_HD;
226 DOSDrives[2].flags = 0;
227 DRIVE_CurDrive = 2;
230 /* Make sure the current drive is valid */
231 if (DRIVE_CurDrive == -1)
233 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
235 if (drive->root && !(drive->flags & DRIVE_DISABLED))
237 DRIVE_CurDrive = i;
238 break;
243 return 1;
247 /***********************************************************************
248 * DRIVE_IsValid
250 int DRIVE_IsValid( int drive )
252 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
253 return (DOSDrives[drive].root &&
254 !(DOSDrives[drive].flags & DRIVE_DISABLED));
258 /***********************************************************************
259 * DRIVE_GetCurrentDrive
261 int DRIVE_GetCurrentDrive(void)
263 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
264 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
265 return DRIVE_CurDrive;
269 /***********************************************************************
270 * DRIVE_SetCurrentDrive
272 int DRIVE_SetCurrentDrive( int drive )
274 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
275 if (!DRIVE_IsValid( drive ))
277 SetLastError( ERROR_INVALID_DRIVE );
278 return 0;
280 TRACE("%c:\n", 'A' + drive );
281 DRIVE_CurDrive = drive;
282 if (pTask) pTask->curdrive = drive | 0x80;
283 return 1;
287 /***********************************************************************
288 * DRIVE_FindDriveRoot
290 * Find a drive for which the root matches the beginning of the given path.
291 * This can be used to translate a Unix path into a drive + DOS path.
292 * Return value is the drive, or -1 on error. On success, path is modified
293 * to point to the beginning of the DOS path.
295 int DRIVE_FindDriveRoot( const char **path )
297 /* idea: check at all '/' positions.
298 * If the device and inode of that path is identical with the
299 * device and inode of the current drive then we found a solution.
300 * If there is another drive pointing to a deeper position in
301 * the file tree, we want to find that one, not the earlier solution.
303 int drive, rootdrive = -1;
304 char buffer[MAX_PATHNAME_LEN];
305 char *next = buffer;
306 const char *p = *path;
307 struct stat st;
309 strcpy( buffer, "/" );
310 for (;;)
312 if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
314 /* Find the drive */
316 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
318 if (!DOSDrives[drive].root ||
319 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
321 if ((DOSDrives[drive].dev == st.st_dev) &&
322 (DOSDrives[drive].ino == st.st_ino))
324 rootdrive = drive;
325 *path = p;
329 /* Get the next path component */
331 *next++ = '/';
332 while ((*p == '/') || (*p == '\\')) p++;
333 if (!*p) break;
334 while (!IS_END_OF_NAME(*p)) *next++ = *p++;
335 *next = 0;
337 *next = 0;
339 if (rootdrive != -1)
340 TRACE("%s -> drive %c:, root='%s', name='%s'\n",
341 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, *path );
342 return rootdrive;
346 /***********************************************************************
347 * DRIVE_GetRoot
349 const char * DRIVE_GetRoot( int drive )
351 if (!DRIVE_IsValid( drive )) return NULL;
352 return DOSDrives[drive].root;
356 /***********************************************************************
357 * DRIVE_GetDosCwd
359 const char * DRIVE_GetDosCwd( int drive )
361 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
362 if (!DRIVE_IsValid( drive )) return NULL;
364 /* Check if we need to change the directory to the new task. */
365 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
366 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
367 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
369 /* Perform the task-switch */
370 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
371 DRIVE_LastTask = GetCurrentTask();
373 return DOSDrives[drive].dos_cwd;
377 /***********************************************************************
378 * DRIVE_GetUnixCwd
380 const char * DRIVE_GetUnixCwd( int drive )
382 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
383 if (!DRIVE_IsValid( drive )) return NULL;
385 /* Check if we need to change the directory to the new task. */
386 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
387 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
388 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
390 /* Perform the task-switch */
391 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
392 DRIVE_LastTask = GetCurrentTask();
394 return DOSDrives[drive].unix_cwd;
398 /***********************************************************************
399 * DRIVE_GetLabel
401 const char * DRIVE_GetLabel( int drive )
403 if (!DRIVE_IsValid( drive )) return NULL;
404 return DOSDrives[drive].label;
408 /***********************************************************************
409 * DRIVE_GetSerialNumber
411 DWORD DRIVE_GetSerialNumber( int drive )
413 if (!DRIVE_IsValid( drive )) return 0;
414 return DOSDrives[drive].serial;
418 /***********************************************************************
419 * DRIVE_SetSerialNumber
421 int DRIVE_SetSerialNumber( int drive, DWORD serial )
423 if (!DRIVE_IsValid( drive )) return 0;
424 DOSDrives[drive].serial = serial;
425 return 1;
429 /***********************************************************************
430 * DRIVE_GetType
432 DRIVETYPE DRIVE_GetType( int drive )
434 if (!DRIVE_IsValid( drive )) return TYPE_INVALID;
435 return DOSDrives[drive].type;
439 /***********************************************************************
440 * DRIVE_GetFlags
442 UINT DRIVE_GetFlags( int drive )
444 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
445 return DOSDrives[drive].flags;
449 /***********************************************************************
450 * DRIVE_Chdir
452 int DRIVE_Chdir( int drive, const char *path )
454 DOS_FULL_NAME full_name;
455 char buffer[MAX_PATHNAME_LEN];
456 LPSTR unix_cwd;
457 BY_HANDLE_FILE_INFORMATION info;
458 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
460 strcpy( buffer, "A:" );
461 buffer[0] += drive;
462 TRACE("(%c:,%s)\n", buffer[0], path );
463 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
465 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
466 if (!FILE_Stat( full_name.long_name, &info )) return 0;
467 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
469 SetLastError( ERROR_FILE_NOT_FOUND );
470 return 0;
472 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
473 while (*unix_cwd == '/') unix_cwd++;
475 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
476 'A' + drive, unix_cwd, full_name.short_name + 3 );
478 HeapFree( SystemHeap, 0, DOSDrives[drive].dos_cwd );
479 HeapFree( SystemHeap, 0, DOSDrives[drive].unix_cwd );
480 DOSDrives[drive].dos_cwd = HEAP_strdupA( SystemHeap, 0,
481 full_name.short_name + 3 );
482 DOSDrives[drive].unix_cwd = HEAP_strdupA( SystemHeap, 0, unix_cwd );
484 if (pTask && (pTask->curdrive & 0x80) &&
485 ((pTask->curdrive & ~0x80) == drive))
487 lstrcpynA( pTask->curdir, full_name.short_name + 2,
488 sizeof(pTask->curdir) );
489 DRIVE_LastTask = GetCurrentTask();
491 return 1;
495 /***********************************************************************
496 * DRIVE_Disable
498 int DRIVE_Disable( int drive )
500 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
502 SetLastError( ERROR_INVALID_DRIVE );
503 return 0;
505 DOSDrives[drive].flags |= DRIVE_DISABLED;
506 return 1;
510 /***********************************************************************
511 * DRIVE_Enable
513 int DRIVE_Enable( int drive )
515 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
517 SetLastError( ERROR_INVALID_DRIVE );
518 return 0;
520 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
521 return 1;
525 /***********************************************************************
526 * DRIVE_SetLogicalMapping
528 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
530 /* If new_drive is already valid, do nothing and return 0
531 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
533 DOSDRIVE *old, *new;
535 old = DOSDrives + existing_drive;
536 new = DOSDrives + new_drive;
538 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
539 !old->root ||
540 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
542 SetLastError( ERROR_INVALID_DRIVE );
543 return 0;
546 if ( new->root )
548 TRACE("Can\'t map drive %c to drive %c - drive %c already exists\n",
549 'A' + existing_drive, 'A' + new_drive, 'A' + new_drive );
550 /* it is already mapped there, so return success */
551 if (!strcmp(old->root,new->root))
552 return 1;
553 return 0;
556 new->root = HEAP_strdupA( SystemHeap, 0, old->root );
557 new->dos_cwd = HEAP_strdupA( SystemHeap, 0, old->dos_cwd );
558 new->unix_cwd = HEAP_strdupA( SystemHeap, 0, old->unix_cwd );
559 memcpy ( new->label, old->label, 12 );
560 new->serial = old->serial;
561 new->type = old->type;
562 new->flags = old->flags;
563 new->dev = old->dev;
564 new->ino = old->ino;
566 TRACE("Drive %c is now equal to drive %c\n",
567 'A' + new_drive, 'A' + existing_drive );
569 return 1;
573 /***********************************************************************
574 * DRIVE_OpenDevice
576 * Open the drive raw device and return a Unix fd (or -1 on error).
578 int DRIVE_OpenDevice( int drive, int flags )
580 if (!DRIVE_IsValid( drive )) return -1;
581 return open( DOSDrives[drive].device, flags );
585 /***********************************************************************
586 * DRIVE_RawRead
588 * Read raw sectors from a device
590 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
592 int fd;
594 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
596 lseek( fd, begin * 512, SEEK_SET );
597 /* FIXME: check errors */
598 read( fd, dataptr, nr_sect * 512 );
599 close( fd );
601 else
603 memset(dataptr, 0, nr_sect * 512);
604 if (fake_success)
606 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
607 if (begin == 1) *dataptr = 0xf8;
609 else
610 return 0;
612 return 1;
616 /***********************************************************************
617 * DRIVE_RawWrite
619 * Write raw sectors to a device
621 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
623 int fd;
625 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
627 lseek( fd, begin * 512, SEEK_SET );
628 /* FIXME: check errors */
629 write( fd, dataptr, nr_sect * 512 );
630 close( fd );
632 else
633 if (!(fake_success))
634 return 0;
636 return 1;
640 /***********************************************************************
641 * DRIVE_GetFreeSpace
643 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
644 PULARGE_INTEGER available )
646 struct statfs info;
647 unsigned long long bigsize,bigavail=0;
649 if (!DRIVE_IsValid(drive))
651 SetLastError( ERROR_INVALID_DRIVE );
652 return 0;
655 /* FIXME: add autoconf check for this */
656 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
657 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
658 #else
659 if (statfs( DOSDrives[drive].root, &info) < 0)
660 #endif
662 FILE_SetDosError();
663 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
664 return 0;
667 bigsize = (unsigned long long)info.f_bsize
668 * (unsigned long long)info.f_blocks;
669 #ifdef STATFS_HAS_BAVAIL
670 bigavail = (unsigned long long)info.f_bavail
671 * (unsigned long long)info.f_bsize;
672 #else
673 # ifdef STATFS_HAS_BFREE
674 bigavail = (unsigned long long)info.f_bfree
675 * (unsigned long long)info.f_bsize;
676 # else
677 # error "statfs has no bfree/bavail member!"
678 # endif
679 #endif
680 size->s.LowPart = (DWORD)bigsize;
681 size->s.HighPart = (DWORD)(bigsize>>32);
682 available->s.LowPart = (DWORD)bigavail;
683 available->s.HighPart = (DWORD)(bigavail>>32);
684 return 1;
687 /***********************************************************************
688 * DRIVE_GetCurrentDirectory
689 * Returns "X:\\path\\etc\\".
691 * Despite the API description, return required length including the
692 * terminating null when buffer too small. This is the real behaviour.
695 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
697 UINT ret;
698 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
700 assert(s);
701 ret = strlen(s) + 3; /* length of WHOLE current directory */
702 if (ret >= buflen) return ret + 1;
703 lstrcpynA( buf, "A:\\", MIN( 4, buflen ) );
704 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
705 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
706 return ret;
709 /***********************************************************************
710 * GetDiskFreeSpace16 (KERNEL.422)
712 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
713 LPDWORD sector_bytes, LPDWORD free_clusters,
714 LPDWORD total_clusters )
716 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
717 free_clusters, total_clusters );
721 /***********************************************************************
722 * GetDiskFreeSpace32A (KERNEL32.206)
724 * Fails if expression resulting from current drive's dir and "root"
725 * is not a root dir of the target drive.
727 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
728 * if the corresponding info is unneeded.
730 * FIXME: needs to support UNC names from Win95 OSR2 on.
732 * Behaviour under Win95a:
733 * CurrDir root result
734 * "E:\\TEST" "E:" FALSE
735 * "E:\\" "E:" TRUE
736 * "E:\\" "E" FALSE
737 * "E:\\" "\\" TRUE
738 * "E:\\TEST" "\\" TRUE
739 * "E:\\TEST" ":\\" FALSE
740 * "E:\\TEST" "E:\\" TRUE
741 * "E:\\TEST" "" FALSE
742 * "E:\\" "" FALSE (!)
743 * "E:\\" 0x0 TRUE
744 * "E:\\TEST" 0x0 TRUE (!)
745 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
746 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
748 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
749 LPDWORD sector_bytes, LPDWORD free_clusters,
750 LPDWORD total_clusters )
752 int drive;
753 ULARGE_INTEGER size,available;
754 LPCSTR path;
755 DWORD cluster_sec;
757 if ((!root) || (strcmp(root,"\\") == 0))
758 drive = DRIVE_GetCurrentDrive();
759 else
760 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
762 drive = toupper(root[0]) - 'A';
763 path = &root[2];
764 if (path[0] == '\0')
765 path = DRIVE_GetDosCwd(drive);
766 else
767 if (path[0] == '\\')
768 path++;
769 if (strlen(path)) /* oops, we are in a subdir */
770 return FALSE;
772 else
773 return FALSE;
775 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
777 /* Cap the size and available at 2GB as per specs. */
778 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
780 size.s.HighPart = 0;
781 size.s.LowPart = 0x7fffffff;
783 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
785 available.s.HighPart =0;
786 available.s.LowPart = 0x7fffffff;
788 if (DRIVE_GetType(drive)==TYPE_CDROM) {
789 if (sector_bytes)
790 *sector_bytes = 2048;
791 size.s.LowPart /= 2048;
792 available.s.LowPart /= 2048;
793 } else {
794 if (sector_bytes)
795 *sector_bytes = 512;
796 size.s.LowPart /= 512;
797 available.s.LowPart /= 512;
799 /* fixme: probably have to adjust those variables too for CDFS */
800 cluster_sec = 1;
801 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
803 if (cluster_sectors)
804 *cluster_sectors = cluster_sec;
805 if (free_clusters)
806 *free_clusters = available.s.LowPart / cluster_sec;
807 if (total_clusters)
808 *total_clusters = size.s.LowPart / cluster_sec;
809 return TRUE;
813 /***********************************************************************
814 * GetDiskFreeSpace32W (KERNEL32.207)
816 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
817 LPDWORD sector_bytes, LPDWORD free_clusters,
818 LPDWORD total_clusters )
820 LPSTR xroot;
821 BOOL ret;
823 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
824 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
825 free_clusters, total_clusters );
826 HeapFree( GetProcessHeap(), 0, xroot );
827 return ret;
831 /***********************************************************************
832 * GetDiskFreeSpaceEx32A (KERNEL32.871)
834 * This function is used to aquire the size of the available and
835 * total space on a logical volume.
837 * RETURNS
839 * Zero on failure, nonzero upon success. Use GetLastError to obtain
840 * detailed error information.
843 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
844 PULARGE_INTEGER avail,
845 PULARGE_INTEGER total,
846 PULARGE_INTEGER totalfree)
848 int drive;
849 ULARGE_INTEGER size,available;
851 if (!root) drive = DRIVE_GetCurrentDrive();
852 else
854 if ((root[1]) && ((root[1] != ':') || (root[2] != '\\')))
856 FIXME("there are valid root names which are not supported yet\n");
857 /* ..like UNC names, for instance. */
859 WARN("invalid root '%s'\n", root );
860 return FALSE;
862 drive = toupper(root[0]) - 'A';
865 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
867 if (total)
869 total->s.HighPart = size.s.HighPart;
870 total->s.LowPart = size.s.LowPart ;
873 if (totalfree)
875 totalfree->s.HighPart = available.s.HighPart;
876 totalfree->s.LowPart = available.s.LowPart ;
879 if (avail)
881 if (FIXME_ON(dosfs))
883 /* On Windows2000, we need to check the disk quota
884 allocated for the user owning the calling process. We
885 don't want to be more obtrusive than necessary with the
886 FIXME messages, so don't print the FIXME unless Wine is
887 actually masquerading as Windows2000. */
889 OSVERSIONINFOA ovi;
890 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
891 if (GetVersionExA(&ovi))
893 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
894 FIXME("no per-user quota support yet\n");
898 /* Quick hack, should eventually be fixed to work 100% with
899 Windows2000 (see comment above). */
900 avail->s.HighPart = available.s.HighPart;
901 avail->s.LowPart = available.s.LowPart ;
904 return TRUE;
907 /***********************************************************************
908 * GetDiskFreeSpaceEx32W (KERNEL32.873)
910 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
911 PULARGE_INTEGER total,
912 PULARGE_INTEGER totalfree)
914 LPSTR xroot;
915 BOOL ret;
917 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
918 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
919 HeapFree( GetProcessHeap(), 0, xroot );
920 return ret;
923 /***********************************************************************
924 * GetDriveType16 (KERNEL.136)
925 * This functions returns the drivetype of a drive in Win16.
926 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
927 * remote drive API. The returnvalue DRIVE_REMOTE for CD-ROMs has been
928 * verified on Win3.11 and Windows 95. Some programs rely on it, so don't
929 * do any pseudo-clever changes.
931 * RETURNS
932 * drivetype DRIVE_xxx
934 UINT16 WINAPI GetDriveType16(
935 UINT16 drive /* [in] number (NOT letter) of drive */
937 TRACE("(%c:)\n", 'A' + drive );
938 switch(DRIVE_GetType(drive))
940 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
941 case TYPE_HD: return DRIVE_FIXED;
942 case TYPE_CDROM: return DRIVE_REMOTE;
943 case TYPE_NETWORK: return DRIVE_REMOTE;
944 case TYPE_INVALID:
945 default: return DRIVE_CANNOTDETERMINE;
950 /***********************************************************************
951 * GetDriveType32A (KERNEL32.208)
953 * Returns the type of the disk drive specified. If root is NULL the
954 * root of the current directory is used.
956 * RETURNS
958 * Type of drive (from Win32 SDK):
960 * DRIVE_UNKNOWN unable to find out anything about the drive
961 * DRIVE_NO_ROOT_DIR nonexistand root dir
962 * DRIVE_REMOVABLE the disk can be removed from the machine
963 * DRIVE_FIXED the disk can not be removed from the machine
964 * DRIVE_REMOTE network disk
965 * DRIVE_CDROM CDROM drive
966 * DRIVE_RAMDISK virtual disk in ram
968 * DRIVE_DOESNOTEXIST XXX Not valid return value
969 * DRIVE_CANNOTDETERMINE XXX Not valid return value
971 * BUGS
973 * Currently returns DRIVE_DOESNOTEXIST and DRIVE_CANNOTDETERMINE
974 * when it really should return DRIVE_NO_ROOT_DIR and DRIVE_UNKNOWN.
975 * Why where the former defines used?
977 * DRIVE_RAMDISK is unsupported.
979 UINT WINAPI GetDriveTypeA(LPCSTR root /* String describing drive */)
981 int drive;
982 TRACE("(%s)\n", debugstr_a(root));
984 if (NULL == root) drive = DRIVE_GetCurrentDrive();
985 else
987 if ((root[1]) && (root[1] != ':'))
989 WARN("invalid root '%s'\n", debugstr_a(root));
990 return DRIVE_DOESNOTEXIST;
992 drive = toupper(root[0]) - 'A';
994 switch(DRIVE_GetType(drive))
996 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
997 case TYPE_HD: return DRIVE_FIXED;
998 case TYPE_CDROM: return DRIVE_CDROM;
999 case TYPE_NETWORK: return DRIVE_REMOTE;
1000 case TYPE_INVALID: return DRIVE_DOESNOTEXIST;
1001 default: return DRIVE_CANNOTDETERMINE;
1006 /***********************************************************************
1007 * GetDriveType32W (KERNEL32.209)
1009 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1011 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1012 UINT ret = GetDriveTypeA( xpath );
1013 HeapFree( GetProcessHeap(), 0, xpath );
1014 return ret;
1018 /***********************************************************************
1019 * GetCurrentDirectory16 (KERNEL.411)
1021 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1023 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1027 /***********************************************************************
1028 * GetCurrentDirectory32A (KERNEL32.196)
1030 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1032 UINT ret;
1033 char longname[MAX_PATHNAME_LEN];
1034 char shortname[MAX_PATHNAME_LEN];
1035 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1036 if ( ret > MAX_PATHNAME_LEN ) {
1037 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1038 return ret;
1040 GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1041 ret = lstrlenA( longname ) + 1;
1042 if (ret > buflen) return ret;
1043 lstrcpyA(buf, longname);
1044 return ret - 1;
1047 /***********************************************************************
1048 * GetCurrentDirectory32W (KERNEL32.197)
1050 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1052 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1053 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1054 if (ret < buflen) lstrcpyAtoW ( buf, xpath );
1055 HeapFree( GetProcessHeap(), 0, xpath );
1056 return ret;
1060 /***********************************************************************
1061 * SetCurrentDirectory (KERNEL.412)
1063 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1065 return SetCurrentDirectoryA( dir );
1069 /***********************************************************************
1070 * SetCurrentDirectory32A (KERNEL32.479)
1072 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1074 int olddrive, drive = DRIVE_GetCurrentDrive();
1076 if (!dir) {
1077 ERR_(file)("(NULL)!\n");
1078 return FALSE;
1080 if (dir[0] && (dir[1]==':'))
1082 drive = tolower( *dir ) - 'a';
1083 dir += 2;
1086 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1087 sets pTask->curdir only if pTask->curdrive is drive */
1088 olddrive = drive; /* in case DRIVE_Chdir fails */
1089 if (!(DRIVE_SetCurrentDrive( drive )))
1090 return FALSE;
1091 /* FIXME: what about empty strings? Add a \\ ? */
1092 if (!DRIVE_Chdir( drive, dir )) {
1093 DRIVE_SetCurrentDrive(olddrive);
1094 return FALSE;
1096 return TRUE;
1100 /***********************************************************************
1101 * SetCurrentDirectory32W (KERNEL32.480)
1103 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1105 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1106 BOOL res = SetCurrentDirectoryA( dir );
1107 HeapFree( GetProcessHeap(), 0, dir );
1108 return res;
1112 /***********************************************************************
1113 * GetLogicalDriveStrings32A (KERNEL32.231)
1115 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1117 int drive, count;
1119 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1120 if (DRIVE_IsValid(drive)) count++;
1121 if ((count * 4) + 1 <= len)
1123 LPSTR p = buffer;
1124 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1125 if (DRIVE_IsValid(drive))
1127 *p++ = 'a' + drive;
1128 *p++ = ':';
1129 *p++ = '\\';
1130 *p++ = '\0';
1132 *p = '\0';
1133 return count * 4;
1135 else
1136 return (count * 4) + 1;/* account for terminating null */
1137 /* The API tells about these different return values */
1141 /***********************************************************************
1142 * GetLogicalDriveStrings32W (KERNEL32.232)
1144 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1146 int drive, count;
1148 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1149 if (DRIVE_IsValid(drive)) count++;
1150 if (count * 4 * sizeof(WCHAR) <= len)
1152 LPWSTR p = buffer;
1153 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1154 if (DRIVE_IsValid(drive))
1156 *p++ = (WCHAR)('a' + drive);
1157 *p++ = (WCHAR)':';
1158 *p++ = (WCHAR)'\\';
1159 *p++ = (WCHAR)'\0';
1161 *p = (WCHAR)'\0';
1163 return count * 4 * sizeof(WCHAR);
1167 /***********************************************************************
1168 * GetLogicalDrives (KERNEL32.233)
1170 DWORD WINAPI GetLogicalDrives(void)
1172 DWORD ret = 0;
1173 int drive;
1175 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1176 if (DRIVE_IsValid(drive)) ret |= (1 << drive);
1177 return ret;
1181 /***********************************************************************
1182 * GetVolumeInformation32A (KERNEL32.309)
1184 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1185 DWORD label_len, DWORD *serial,
1186 DWORD *filename_len, DWORD *flags,
1187 LPSTR fsname, DWORD fsname_len )
1189 int drive;
1190 char *cp;
1192 /* FIXME, SetLastErrors missing */
1194 if (!root) drive = DRIVE_GetCurrentDrive();
1195 else
1197 if ((root[1]) && (root[1] != ':'))
1199 WARN("invalid root '%s'\n",root);
1200 return FALSE;
1202 drive = toupper(root[0]) - 'A';
1204 if (!DRIVE_IsValid( drive )) return FALSE;
1205 if (label)
1207 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1208 for (cp = label; *cp; cp++);
1209 while (cp != label && *(cp-1) == ' ') cp--;
1210 *cp = '\0';
1212 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1214 /* Set the filesystem information */
1215 /* Note: we only emulate a FAT fs at the present */
1217 if (filename_len) {
1218 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1219 *filename_len = 12;
1220 else
1221 *filename_len = 255;
1223 if (flags)
1225 *flags=0;
1226 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1227 *flags|=FS_CASE_SENSITIVE;
1228 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1229 *flags|=FS_CASE_IS_PRESERVED ;
1231 if (fsname) {
1232 /* Diablo checks that return code ... */
1233 if (DRIVE_GetType(drive)==TYPE_CDROM)
1234 lstrcpynA( fsname, "CDFS", fsname_len );
1235 else
1236 lstrcpynA( fsname, "FAT", fsname_len );
1238 return TRUE;
1242 /***********************************************************************
1243 * GetVolumeInformation32W (KERNEL32.310)
1245 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1246 DWORD label_len, DWORD *serial,
1247 DWORD *filename_len, DWORD *flags,
1248 LPWSTR fsname, DWORD fsname_len )
1250 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1251 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1252 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1253 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1254 filename_len, flags, xfsname,
1255 fsname_len );
1256 if (ret)
1258 if (label) lstrcpyAtoW( label, xvolname );
1259 if (fsname) lstrcpyAtoW( fsname, xfsname );
1261 HeapFree( GetProcessHeap(), 0, xroot );
1262 HeapFree( GetProcessHeap(), 0, xvolname );
1263 HeapFree( GetProcessHeap(), 0, xfsname );
1264 return ret;
1267 /***********************************************************************
1268 * SetVolumeLabelA (KERNEL32.675)
1270 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1272 int drive;
1274 /* FIXME, SetLastErrors missing */
1276 if (!root) drive = DRIVE_GetCurrentDrive();
1277 else
1279 if ((root[1]) && (root[1] != ':'))
1281 WARN("invalid root '%s'\n",root);
1282 return FALSE;
1284 drive = toupper(root[0]) - 'A';
1286 if (!DRIVE_IsValid( drive )) return FALSE;
1288 /* some copy protection stuff check this */
1289 if (DRIVE_GetType( drive ) == TYPE_CDROM) return FALSE;
1291 FIXME("(%s,%s),stub!\n", root, volname);
1292 return TRUE;
1295 /***********************************************************************
1296 * SetVolumeLabelW (KERNEL32.676)
1298 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1300 LPSTR xroot, xvol;
1301 BOOL ret;
1303 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1304 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1305 ret = SetVolumeLabelA( xroot, xvol );
1306 HeapFree( GetProcessHeap(), 0, xroot );
1307 HeapFree( GetProcessHeap(), 0, xvol );
1308 return ret;