/* fhandler_cygdrive.cc This file is part of Cygwin. This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include "winsup.h" #include #include #include "cygerrno.h" #include "security.h" #include "path.h" #include "fhandler.h" #include "dtable.h" #include "cygheap.h" #include "shared_info.h" #define _LIBC #include fhandler_cygdrive::fhandler_cygdrive () : fhandler_disk_file () { } int fhandler_cygdrive::open (int flags, mode_t mode) { if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) { set_errno (EEXIST); return 0; } if (flags & O_WRONLY) { set_errno (EISDIR); return 0; } /* Open a fake handle to \\Device\\Null */ return open_null (flags); } int fhandler_cygdrive::fstat (struct stat *buf) { fhandler_base::fstat (buf); buf->st_ino = 2; buf->st_mode = S_IFDIR | STD_RBITS | STD_XBITS; buf->st_nlink = 1; return 0; } int fhandler_cygdrive::fstatvfs (struct statvfs *sfs) { /* Virtual file system. Just return an empty buffer with a few values set to something useful. Just as on Linux. */ memset (sfs, 0, sizeof (*sfs)); sfs->f_bsize = sfs->f_frsize = 4096; sfs->f_flag = ST_RDONLY; sfs->f_namemax = NAME_MAX; return 0; } #define MAX_DRIVE_BUF_LEN (sizeof ("x:\\") * 26 + 2) struct __DIR_drives { char *pdrive; char pbuf[MAX_DRIVE_BUF_LEN]; }; #define d_drives(d) ((__DIR_drives *) (d)->__d_internal) DIR * fhandler_cygdrive::opendir (int fd) { DIR *dir; dir = fhandler_disk_file::opendir (fd); if (dir) { dir->__d_internal = (uintptr_t) new __DIR_drives; GetLogicalDriveStrings (MAX_DRIVE_BUF_LEN, d_drives(dir)->pbuf); d_drives(dir)->pdrive = d_drives(dir)->pbuf; } return dir; } int fhandler_cygdrive::readdir (DIR *dir, dirent *de) { WCHAR drive[] = L"X:"; while (true) { if (!d_drives(dir)->pdrive || !*d_drives(dir)->pdrive) { if (!(dir->__flags & dirent_saw_dot)) { de->d_name[0] = '.'; de->d_name[1] = '\0'; de->d_ino = 2; } return ENMFILE; } disk_type dt = get_disk_type ((drive[0] = *d_drives(dir)->pdrive, drive)); if (dt == DT_SHARE_SMB) { /* Calling NetUseGetInfo on SMB drives allows to fetch the current state of the drive without trying to open a file descriptor on the share (GetFileAttributes). This avoids waiting for SMB timeouts. Of course, there's a downside: If a drive becomes availabe again, it can take a couple of minutes to recognize it. As long as this didn't happen, the drive will not show up in the cygdrive dir. */ PUSE_INFO_1 pui1; DWORD status; if (NetUseGetInfo (NULL, drive, 1, (PBYTE *) &pui1) == NERR_Success) { status = pui1->ui1_status; NetApiBufferFree (pui1); if (status == USE_OK) break; } } else if (dt != DT_FLOPPY && GetFileAttributes (d_drives(dir)->pdrive) != INVALID_FILE_ATTRIBUTES) break; d_drives(dir)->pdrive = strchr (d_drives(dir)->pdrive, '\0') + 1; } *de->d_name = cyg_tolower (*d_drives(dir)->pdrive); de->d_name[1] = '\0'; user_shared->warned_msdos = true; de->d_ino = readdir_get_ino (d_drives(dir)->pdrive, false); dir->__d_position++; d_drives(dir)->pdrive = strchr (d_drives(dir)->pdrive, '\0') + 1; syscall_printf ("%p = readdir (%p) (%s)", &de, dir, de->d_name); return 0; } void fhandler_cygdrive::rewinddir (DIR *dir) { d_drives(dir)->pdrive = d_drives(dir)->pbuf; dir->__d_position = 0; } int fhandler_cygdrive::closedir (DIR *dir) { delete d_drives(dir); return 0; }