diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 1cb69b9ef..778f13fe6 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,57 @@ +2006-01-26 Corinna Vinschen + + * autoload.cc (NtQueryDirectoryFile): Define. + * dir.cc (__opendir_with_d_ino): Just call opendir. + (opendir): Remove CYGWIN_VERSION_CHECK_FOR_NEEDS_D_INO handling. + (readdir_worker): Only try generating d_ino if it's 0. + Utilize namehash of directories fhandler. Call readdir_get_ino to + generate d_ino for "..". + (seekdir64): Keep dirent_set_d_ino flag. + * fhandler.h (enum dirent_states): Add dirent_get_d_ino. + (class fhandler_disk_file): Declare new private methods readdir_helper + and readdir_9x. + * fhandler_disk_file.cc (path_conv::hasgood_inode): New method to + evaluate if a filesystem has reliable inode numbers. + (fhandler_base::fstat_by_handle): Accomodate structure member name + change from IndexNumber to FileId. + (fhandler_base::fstat_helper): Call hasgood_inode here. + (fhandler_disk_file::opendir): Call fhaccess only for real files. + Don't append '*' to __d_dirname here, move to readdir_9x. On NT, + open directory handle here. Set dirent_get_d_ino and dirent_set_d_ino + flags according to wincap and filesystem. + (fhandler_disk_file::readdir_helper): New method to implement readdir + postprocessing only once. + (readdir_get_ino_by_handle): New static function. + (readdir_get_ino): New function to centralize inode number evaluation + in case inode number hasn't been returned by NtQueryDirectoryFile. + (fhandler_disk_file::readdir): Move old functionality to readdir_9x. + Call readdir_9x when on 9x/Me. Implement NT specific readdir here. + (fhandler_disk_file::readdir_9x): Move 9x specific readdir here. + (fhandler_disk_file::seekdir): Accomodate new NT readdir method. + (fhandler_disk_file::closedir): Ditto. + (fhandler_cygdrive::fstat): Set d_ino to namehash. Add comment. + (fhandler_cygdrive::opendir): Call get_namehash to prepare later + correct evaluation of d_ino. + (fhandler_cygdrive::readdir): Replace recursion with loop. Evaluate + drive's d_ino by calling readdir_get_ino. + * fhandler_proc.cc (fhandler_proc::readdir): Set dirent_saw_dot and + dirent_saw_dot_dot to avoid seeing . and .. entries twice. + * fhandler_process.cc (fhandler_process::readdir): Ditto. + * fhandler_registry.cc (fhandler_registry::readdir): Ditto. + * ntdll.h (STATUS_INVALID_PARAMETER): New define. + (STATUS_INVALID_LEVEL): New define. + (struct _FILE_INTERNAL_INFORMATION): Rename member IndexNumber to + FileId (as in Nebbitt). + * path.h (path_conv::hasgood_inode): Now implemented in + fhandler_disk_file.cc. + * wincap.h (wincaps::has_fileid_dirinfo): New element. + * wincap.cc: Implement above element throughout. + * winsup.h (readdir_get_ino): Add declaration. + * include/sys/dirent.h (struct dirent): Slightly rename structure + members to accomodate changes. + Remove __USE_EXPENSIVE_CYGWIN_D_INO handling and declaration of + __opendir_with_d_ino. + 2006-01-27 Christopher Faylor * spawn.cc (spawn_guts): Fix potential handle leak when failing exec. diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc index 0d76ebeae..333bff56d 100644 --- a/winsup/cygwin/autoload.cc +++ b/winsup/cygwin/autoload.cc @@ -1,6 +1,6 @@ /* autoload.cc: all dynamic load stuff. - Copyright 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. + Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006 Red Hat, Inc. This file is part of Cygwin. @@ -385,6 +385,7 @@ LoadDLLfuncEx (NtLockVirtualMemory, 16, ntdll, 1) LoadDLLfuncEx (NtMapViewOfSection, 40, ntdll, 1) LoadDLLfuncEx (NtOpenFile, 24, ntdll, 1) LoadDLLfuncEx (NtOpenSection, 12, ntdll, 1) +LoadDLLfuncEx2 (NtQueryDirectoryFile, 44, ntdll, 1, 1) LoadDLLfuncEx2 (NtQueryInformationFile, 20, ntdll, 1, 1) LoadDLLfuncEx (NtQueryInformationProcess, 20, ntdll, 1) LoadDLLfuncEx2 (NtQueryObject, 20, ntdll, 1, 1) diff --git a/winsup/cygwin/dir.cc b/winsup/cygwin/dir.cc index 2e3901dab..eb665b91f 100644 --- a/winsup/cygwin/dir.cc +++ b/winsup/cygwin/dir.cc @@ -42,13 +42,12 @@ dirfd (DIR *dir) return dir->__d_fd; } +/* Symbol kept for backward compatibility. Don't remove. Don't declare + in public header file. */ extern "C" DIR * __opendir_with_d_ino (const char *name) { - DIR *res = opendir (name); - if (res) - res->__flags |= dirent_set_d_ino; - return res; + return opendir (name); } /* opendir: POSIX 5.1.2.1 */ @@ -69,9 +68,7 @@ opendir (const char *name) res = NULL; } - if (res) - res->__flags |= CYGWIN_VERSION_CHECK_FOR_NEEDS_D_INO ? dirent_set_d_ino : 0; - else if (fh) + if (!res && fh) delete fh; return res; } @@ -89,6 +86,7 @@ readdir_worker (DIR *dir, dirent *de) return EBADF; } + de->d_ino = 0; int res = ((fhandler_base *) dir->__fh)->readdir (dir, de); if (res == ENMFILE) @@ -109,39 +107,38 @@ readdir_worker (DIR *dir, dirent *de) } } - if (!res) + if (!res && !de->d_ino) { - /* Compute d_ino by combining filename hash with the directory hash - (which was stored in dir->__d_dirhash when opendir was called). */ - if (de->d_name[0] != '.') - /* relax */; - else if (de->d_name[1] == '\0') - dir->__flags |= dirent_saw_dot; - else if (de->d_name[1] == '.' && de->d_name[2] == '\0') - dir->__flags |= dirent_saw_dot_dot; - if (!(dir->__flags & dirent_set_d_ino)) - de->__dirent_internal = 0; - else + bool is_dot = false; + bool is_dot_dot = false; + + if (de->d_name[0] == '.') { -#if 0 - size_t len = strlen (dir->__d_dirname) + strlen (de->d_name); - char *path = (char *) alloca (len); - char *p = strchr (strcpy (path, dir->__d_dirname), '\0'); - strcpy (p - 1, de->d_name); - struct __stat64 st; - if (lstat64 (path, &st) == 0) - de->__dirent_internal = st.st_ino; - else - { -#endif - de->__dirent_internal = hash_path_name (0, dir->__d_dirname); - de->__dirent_internal = hash_path_name (de->__dirent_internal, de->d_name); -#if 0 - } -#endif + if (de->d_name[1] == '\0') + is_dot = true; + else if (de->d_name[1] == '.' && de->d_name[2] == '\0') + is_dot_dot = true; + } + + if (is_dot_dot && !(dir->__flags & dirent_isroot)) + de->d_ino = readdir_get_ino (dir, + ((fhandler_base *) dir->__fh)->get_name (), + true); + else + { + /* Compute d_ino by combining filename hash with directory hash. */ + de->d_ino = ((fhandler_base *) dir->__fh)->get_namehash (); + if (!is_dot && !is_dot_dot) + { + const char *w32name = ((fhandler_base *) dir->__fh)->get_win32_name (); + /* A drive's root dir has a trailing backslash already. */ + if (w32name[1] != ':' || w32name[2] != '\\' || w32name[3]) + de->d_ino = hash_path_name (de->d_ino, "\\"); + de->d_ino = hash_path_name (de->d_ino, de->d_name); + } } - de->__dirent_internal1 = de->__dirent_internal; } + de->__d_internal1 = de->d_ino; return res; } @@ -201,7 +198,7 @@ seekdir64 (DIR *dir, _off64_t loc) if (dir->__d_cookie != __DIRENT_COOKIE) return; - dir->__flags &= (dirent_isroot | dirent_set_d_ino); + dir->__flags &= (dirent_isroot | dirent_get_d_ino | dirent_set_d_ino); return ((fhandler_base *) dir->__fh)->seekdir (dir, loc); } diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 525e37ce8..82152019b 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -48,7 +48,8 @@ enum dirent_states dirent_saw_cygdrive = 0x0010, dirent_saw_dev = 0x0020, dirent_saw_proc = 0x0040, - dirent_set_d_ino = 0x0080 + dirent_set_d_ino = 0x0080, + dirent_get_d_ino = 0x0100 }; enum conn_state @@ -650,6 +651,8 @@ class fhandler_dev_tape: public fhandler_dev_raw class fhandler_disk_file: public fhandler_base { void touch_ctime (); + int readdir_helper (DIR *, dirent *, DWORD, DWORD, char *) __attribute__ ((regparm (3))); + int readdir_9x (DIR *, dirent *) __attribute__ ((regparm (3))); public: fhandler_disk_file (); diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc index b534b7fb0..d6b1cd282 100644 --- a/winsup/cygwin/fhandler_disk_file.cc +++ b/winsup/cygwin/fhandler_disk_file.cc @@ -1,7 +1,7 @@ /* fhandler_disk_file.cc Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, - 2005 Red Hat, Inc. + 2005, 2006 Red Hat, Inc. This file is part of Cygwin. @@ -96,6 +96,38 @@ path_conv::ndisk_links (DWORD nNumberOfLinks) return count + saw_dot; } +bool +path_conv::hasgood_inode () +{ + /* Assume that if a drive has ACL support it MAY have valid "inodes". + It definitely does not have valid inodes if it does not have ACL + support. Decouple from has_acls() which follows smbntsec setting. */ + if (!(fs_flags () & FILE_PERSISTENT_ACLS) || drive_type () == DRIVE_UNKNOWN) + return false; + if (drive_type () == DRIVE_REMOTE) + { + /* From own experiments and replies from the Cygwin mailing list, + we're now trying to figure out how to determine remote file + systems which are capable of returning persistent inode + numbers. It seems that NT4 NTFS, when accessed remotly, and + some other remote file systems return unreliable values in + nFileIndex. The common factor of these unreliable remote FS + seem to be that FILE_SUPPORTS_OBJECT_IDS isn't set, even though + this should have nothing to do with inode numbers. + An exception is Samba, which seems to return valid inode numbers + without having the FILE_SUPPORTS_OBJECT_IDS flag set. So we're + testing for the flag values returned by a 3.x Samba explicitely + for now. */ +#define FS_IS_SAMBA (FILE_CASE_SENSITIVE_SEARCH \ + | FILE_CASE_PRESERVED_NAMES \ + | FILE_PERSISTENT_ACLS) + if (!(fs_flags () & FILE_SUPPORTS_OBJECT_IDS) + && fs_flags () != FS_IS_SAMBA) + return false; + } + return true; +} + int __stdcall fhandler_base::fstat_by_handle (struct __stat64 *buf) { @@ -139,8 +171,8 @@ fhandler_base::fstat_by_handle (struct __stat64 *buf) pfai->StandardInformation.EndOfFile.HighPart, pfai->StandardInformation.EndOfFile.LowPart, pfai->StandardInformation.AllocationSize.QuadPart, - pfai->InternalInformation.IndexNumber.HighPart, - pfai->InternalInformation.IndexNumber.LowPart, + pfai->InternalInformation.FileId.HighPart, + pfai->InternalInformation.FileId.LowPart, pfai->StandardInformation.NumberOfLinks, pfai->BasicInformation.FileAttributes); } @@ -315,53 +347,11 @@ fhandler_base::fstat_helper (struct __stat64 *buf, let's try it with `1' as link count. */ buf->st_nlink = pc.ndisk_links (nNumberOfLinks); - /* Assume that if a drive has ACL support it MAY have valid "inodes". - It definitely does not have valid inodes if it does not have ACL - support. Decouple from has_acls() which follows smbntsec setting. */ - switch ((pc.fs_flags () & FILE_PERSISTENT_ACLS) - && (nFileIndexHigh || nFileIndexLow) - ? pc.drive_type () : DRIVE_UNKNOWN) - { - case DRIVE_FIXED: - case DRIVE_REMOVABLE: - case DRIVE_CDROM: - case DRIVE_RAMDISK: - /* Although the documentation indicates otherwise, it seems like - "inodes" on these devices are persistent, at least across reboots. */ - buf->st_ino = (((__ino64_t) nFileIndexHigh) << 32) - | (__ino64_t) nFileIndexLow; - break; - - case DRIVE_REMOTE: - /* From own experiments and replies from the Cygwin mailing list, - we're now trying to figure out how to determine remote file - systems which are capable of returning persistent inode - numbers. It seems that NT4 NTFS, when accessed remotly, and - some other remote file systems return unreliable values in - nFileIndex. The common factor of these unreliable remote FS - seem to be that FILE_SUPPORTS_OBJECT_IDS isn't set, even though - this should have nothing to do with inode numbers. - An exception is Samba, which seems to return valid inode numbers - without having the FILE_SUPPORTS_OBJECT_IDS flag set. So we're - testing for the flag values returned by a 3.x Samba explicitely - for now. */ -#define FS_IS_SAMBA (FILE_CASE_SENSITIVE_SEARCH \ - | FILE_CASE_PRESERVED_NAMES \ - | FILE_PERSISTENT_ACLS) - if ((pc.fs_flags () & FILE_SUPPORTS_OBJECT_IDS) - || pc.fs_flags () == FS_IS_SAMBA) - { - buf->st_ino = (((__ino64_t) nFileIndexHigh) << 32) - | (__ino64_t) nFileIndexLow; - break; - } - /*FALLTHRU*/ - default: - /* Either the nFileIndex* fields are unreliable or unavailable. Use the - next best alternative. */ - buf->st_ino = get_namehash (); - break; - } + if (pc.hasgood_inode ()) + buf->st_ino = (((__ino64_t) nFileIndexHigh) << 32) + | (__ino64_t) nFileIndexLow; + else + buf->st_ino = get_namehash (); buf->st_blksize = S_BLKSIZE; @@ -1376,7 +1366,7 @@ fhandler_disk_file::opendir () set_errno (ENOMEM); goto free_dirname; } - else if (fhaccess (R_OK) != 0) + else if (!pc.isspecial () && fhaccess (R_OK) != 0) goto free_dirent; else { @@ -1391,18 +1381,36 @@ fhandler_disk_file::opendir () fd->nohandle (true); dir->__d_fd = fd; dir->__fh = this; - /* FindFirstFile doesn't seem to like duplicate /'s. */ + /* FindFirstFile doesn't seem to like duplicate /'s. + The dirname is generated with trailing backslash here which + simplifies later usage of dirname for checking symlinks. + Appending a "*" is moved right before calling FindFirstFile. + Since FindFirstFile is only called once, this should even be a + teeny little bit faster. */ len = strlen (dir->__d_dirname); - if (len == 0 || isdirsep (dir->__d_dirname[len - 1])) - strcat (dir->__d_dirname, "*"); - else - strcat (dir->__d_dirname, "\\*"); /**/ + if (len && !isdirsep (dir->__d_dirname[len - 1])) + strcpy (dir->__d_dirname + len, "\\"); dir->__d_cookie = __DIRENT_COOKIE; dir->__handle = INVALID_HANDLE_VALUE; dir->__d_position = 0; - res = dir; dir->__flags = (pc.normalized_path[0] == '/' && pc.normalized_path[1] == '\0') ? dirent_isroot : 0; + if (!pc.isspecial () && wincap.is_winnt ()) + { + dir->__handle = CreateFile (get_win32_name (), GENERIC_READ, + wincap.shared (), NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (dir->__handle == INVALID_HANDLE_VALUE) + { + __seterrno (); + goto free_dirent; + } + if (wincap.has_fileid_dirinfo ()) + dir->__flags |= dirent_get_d_ino; + if (pc.hasgood_inode ()) + dir->__flags |= dirent_set_d_ino; + } + res = dir; } syscall_printf ("%p = opendir (%s)", res, get_name ()); @@ -1417,21 +1425,262 @@ free_dir: return res; } +int +fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err, + DWORD attr, char *fname) +{ + if (w32_err) + { + bool added = false; + if (!(dir->__flags & dirent_isroot)) + /* nothing */; + else if (0 && !(dir->__flags & dirent_saw_dev)) + { + strcpy (fname, "dev"); + added = true; + } + else if (!(dir->__flags & dirent_saw_proc)) + { + strcpy (fname, "proc"); + added = true; + } + else if (!(dir->__flags & dirent_saw_cygdrive) + && mount_table->cygdrive_len > 1) + { + strcpy (fname, mount_table->cygdrive + 1); + fname[mount_table->cygdrive_len - 2] = '\0'; + added = true; + } + + if (added) + { + attr = 0; + dir->__flags &= ~dirent_set_d_ino; + } + else + return geterrno_from_win_error (w32_err); + } + + /* Check for Windows shortcut. If it's a Cygwin or U/WIN + symlink, drop the .lnk suffix. */ + if (attr & FILE_ATTRIBUTE_READONLY) + { + char *c = fname; + int len = strlen (c); + if (strcasematch (c + len - 4, ".lnk")) + { + char fbuf[CYG_MAX_PATH]; + strcpy (fbuf, dir->__d_dirname); + strcat (fbuf, c); + path_conv fpath (fbuf, PC_SYM_NOFOLLOW); + if (fpath.issymlink () || fpath.is_fs_special ()) + c[len - 4] = '\0'; + } + } + + if (pc.isencoded ()) + fnunmunge (de->d_name, fname); + else + strcpy (de->d_name, fname); + if (dir->__flags & dirent_isroot) + { + if (strcasematch (de->d_name, "dev")) + { + dir->__flags |= dirent_saw_dev; + de->d_ino = hash_path_name (0, "/dev"); + } + else if (strcasematch (de->d_name, "proc")) + { + dir->__flags |= dirent_saw_proc; + de->d_ino = hash_path_name (0, "/proc"); + } + if (strlen (de->d_name) == mount_table->cygdrive_len - 2 + && strncasematch (de->d_name, mount_table->cygdrive + 1, + mount_table->cygdrive_len - 2)) + { + dir->__flags |= dirent_saw_cygdrive; + de->d_ino = 0; + } + } + if (dir->__d_position == 0 && !strcmp (fname, ".")) + dir->__flags |= dirent_saw_dot; + else if (dir->__d_position == 1 && !strcmp (fname, "..")) + dir->__flags |= dirent_saw_dot_dot; + return 0; +} + +static inline __ino64_t +readdir_get_ino_by_handle (HANDLE hdl) +{ + IO_STATUS_BLOCK io; + FILE_INTERNAL_INFORMATION pfai; + + if (!NtQueryInformationFile (hdl, &io, &pfai, sizeof pfai, + FileInternalInformation)) + return pfai.FileId.QuadPart; + return 0; +} + +__ino64_t __stdcall +readdir_get_ino (DIR *dir, const char *path, bool dot_dot) +{ + char fname[CYG_MAX_PATH]; + struct __stat64 st; + HANDLE hdl; + __ino64_t ino = 0; + + if (!(dir->__flags & dirent_isroot)) + { + strcpy (fname, path); + if (dot_dot) + strcat (fname, (*fname && fname[strlen (fname) - 1] == '/') + ? ".." : "/.."); + path_conv pc (fname); + if (pc.isspecial ()) + { + if (!lstat64 (fname, &st)) + ino = st.st_ino; + } + else if (!pc.hasgood_inode ()) + ino = hash_path_name (0, pc); + else if ((hdl = CreateFile (pc, GENERIC_READ, wincap.shared (), NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, + NULL)) != INVALID_HANDLE_VALUE) + { + ino = readdir_get_ino_by_handle (hdl); + CloseHandle (hdl); + } + } + else + ino = readdir_get_ino_by_handle (dir->__handle); + return ino; +} + int fhandler_disk_file::readdir (DIR *dir, dirent *de) { - WIN32_FIND_DATA buf; - HANDLE handle; - int res; + int res = 0; + NTSTATUS status = STATUS_SUCCESS; if (!dir->__handle) { res = ENMFILE; goto out; } + + if (!wincap.is_winnt ()) + return readdir_9x (dir, de); + +#define DIR_BUF_SIZE (sizeof (FILE_ID_BOTH_DIR_INFORMATION) + 2 * CYG_MAX_PATH) + PFILE_ID_BOTH_DIR_INFORMATION buf = (PFILE_ID_BOTH_DIR_INFORMATION) + alloca (DIR_BUF_SIZE); + IO_STATUS_BLOCK io; + wchar_t *FileName = buf->FileName; + char fname[CYG_MAX_PATH]; + + if ((dir->__flags & dirent_get_d_ino)) + { + status = NtQueryDirectoryFile (dir->__handle, NULL, NULL, 0, &io, + buf, DIR_BUF_SIZE, + FileIdBothDirectoryInformation, + TRUE, NULL, dir->__d_position == 0); + /* FileIdBothDirectoryInformation isn't supported for remote drives + on NT4 and 2K systems, and it's also not supported on 2K at all, + when accessing network drives on any remote OS. We just fall + back to using a standard directory query in this case and note + this case using the dirent_get_d_ino flag. */ + if (!status) + { + if ((dir->__flags & dirent_set_d_ino)) + de->d_ino = buf->FileId.QuadPart; + } + else if (status == STATUS_INVALID_LEVEL + || status == STATUS_INVALID_PARAMETER) + dir->__flags &= ~dirent_get_d_ino; + } + if (!(dir->__flags & dirent_get_d_ino)) + { + status = NtQueryDirectoryFile (dir->__handle, NULL, NULL, 0, &io, buf, + DIR_BUF_SIZE, FileBothDirectoryInformation, + TRUE, NULL, dir->__d_position == 0); + FileName = ((PFILE_BOTH_DIR_INFORMATION) buf)->FileName; + } + if (!status && de->d_ino == 0 && (dir->__flags & dirent_set_d_ino)) + { + OBJECT_ATTRIBUTES attr; + + if (dir->__d_position == 0 && buf->FileNameLength == 2 + && FileName[0] == '.') + de->d_ino = readdir_get_ino_by_handle (dir->__handle); + else if (dir->__d_position == 1 && buf->FileNameLength == 4 + && FileName[0] == '.' && FileName[1] == '.') + de->d_ino = readdir_get_ino (dir, pc.normalized_path, true); + else + { + HANDLE hdl; + UNICODE_STRING upath = {buf->FileNameLength, CYG_MAX_PATH * 2, + FileName}; + InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE, + dir->__handle , NULL); + if (!NtOpenFile (&hdl, READ_CONTROL, &attr, &io, + wincap.shared (), 0)) + { + de->d_ino = readdir_get_ino_by_handle (hdl); + CloseHandle (hdl); + } + } + } + + if (!status) + { + wcstombs (fname, FileName, buf->FileNameLength / 2); + fname[buf->FileNameLength / 2] = '\0'; + } + + if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status), + buf->FileAttributes, fname))) + dir->__d_position++; + else if (!(dir->__flags & dirent_saw_dot)) + { + strcpy (de->d_name , "."); + de->d_ino = readdir_get_ino_by_handle (dir->__handle); + dir->__d_position++; + dir->__flags |= dirent_saw_dot; + res = 0; + } + else if (!(dir->__flags & dirent_saw_dot)) + { + strcpy (de->d_name , ".."); + de->d_ino = readdir_get_ino (dir, pc.normalized_path, true); + dir->__d_position++; + dir->__flags |= dirent_saw_dot_dot; + res = 0; + } + else + { + CloseHandle (dir->__handle); + dir->__handle = NULL; + } + +out: + syscall_printf ("%d = readdir (%p) (%s)", dir, &de, de->d_name); + return res; +} + +int +fhandler_disk_file::readdir_9x (DIR *dir, dirent *de) +{ + WIN32_FIND_DATA buf; + HANDLE handle; + int res = 0; + BOOL ret = TRUE; + if (dir->__handle == INVALID_HANDLE_VALUE && dir->__d_position == 0) { + int len = strlen (dir->__d_dirname); + strcpy (dir->__d_dirname + len, "*"); handle = FindFirstFileA (dir->__d_dirname, &buf); + dir->__d_dirname[len] = '\0'; DWORD lasterr = GetLastError (); dir->__handle = handle; if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) @@ -1445,76 +1694,18 @@ fhandler_disk_file::readdir (DIR *dir, dirent *de) res = EBADF; goto out; } - else if (!FindNextFileA (dir->__handle, &buf)) - { - bool added = false; - if (!(dir->__flags & dirent_isroot)) - /* nothing */; - else if (0 && !(dir->__flags & dirent_saw_dev)) - { - strcpy (buf.cFileName, "dev"); - added = true; - } - else if (!(dir->__flags & dirent_saw_proc)) - { - strcpy (buf.cFileName, "proc"); - added = true; - } - else if (!(dir->__flags & dirent_saw_cygdrive) - && mount_table->cygdrive_len > 1) - { - strcpy (buf.cFileName, mount_table->cygdrive + 1); - buf.cFileName[mount_table->cygdrive_len - 2] = '\0'; - added = true; - } - - if (added) - buf.dwFileAttributes = 0; - else - { - res = geterrno_from_win_error (); - FindClose (dir->__handle); - dir->__handle = NULL; - goto out; - } - } - - /* Check for Windows shortcut. If it's a Cygwin or U/WIN - symlink, drop the .lnk suffix. */ - if (buf.dwFileAttributes & FILE_ATTRIBUTE_READONLY) - { - char *c = buf.cFileName; - int len = strlen (c); - if (strcasematch (c + len - 4, ".lnk")) - { - char fbuf[CYG_MAX_PATH]; - strcpy (fbuf, dir->__d_dirname); - strcpy (fbuf + strlen (fbuf) - 1, c); - path_conv fpath (fbuf, PC_SYM_NOFOLLOW); - if (fpath.issymlink () || fpath.is_fs_special ()) - c[len - 4] = '\0'; - } - } - - /* We get here if `buf' contains valid data. */ - if (pc.isencoded ()) - fnunmunge (de->d_name, buf.cFileName); else - strcpy (de->d_name, buf.cFileName); - if (dir->__flags & dirent_isroot) + ret = FindNextFileA (dir->__handle, &buf); + + if (!(res = readdir_helper (dir, de, ret ? 0 : GetLastError (), + buf.dwFileAttributes, buf.cFileName))) + dir->__d_position++; + else { - if (strcasematch (de->d_name, "dev")) - dir->__flags |= dirent_saw_dev; - else if (strcasematch (de->d_name, "proc")) - dir->__flags |= dirent_saw_proc; - if (strlen (de->d_name) == mount_table->cygdrive_len - 2 - && strncasematch (de->d_name, mount_table->cygdrive + 1, - mount_table->cygdrive_len - 2)) - dir->__flags |= dirent_saw_cygdrive; + FindClose (dir->__handle); + dir->__handle = NULL; } - dir->__d_position++; - res = 0; out: syscall_printf ("%d = readdir (%p) (%s)", dir, &de, de->d_name); return res; @@ -1538,7 +1729,7 @@ fhandler_disk_file::seekdir (DIR *dir, _off64_t loc) void fhandler_disk_file::rewinddir (DIR *dir) { - if (dir->__handle != INVALID_HANDLE_VALUE) + if (!wincap.is_winnt () && dir->__handle != INVALID_HANDLE_VALUE) { if (dir->__handle) FindClose (dir->__handle); @@ -1552,7 +1743,8 @@ fhandler_disk_file::closedir (DIR *dir) { int res = 0; if (dir->__handle && dir->__handle != INVALID_HANDLE_VALUE - && FindClose (dir->__handle) == 0) + && ((wincap.is_winnt () && !CloseHandle (dir->__handle)) + || (!wincap.is_winnt () && !FindClose (dir->__handle)))) { __seterrno (); res = -1; @@ -1580,6 +1772,9 @@ int fhandler_cygdrive::fstat (struct __stat64 *buf) { buf->st_mode = S_IFDIR | 0555; + /* Call get_namehash before calling set_drives, otherwise the namehash + is broken due to overwriting the win32 path in set_drives. */ + buf->st_ino = get_namehash (); if (!ndrives) set_drives (); buf->st_nlink = ndrives + 2; @@ -1592,6 +1787,9 @@ fhandler_cygdrive::opendir () DIR *dir; dir = fhandler_disk_file::opendir (); + /* Call get_namehash before calling set_drives, otherwise the namehash + is broken due to overwriting the win32 path in set_drives. */ + get_namehash (); if (dir && !ndrives) set_drives (); @@ -1601,16 +1799,17 @@ fhandler_cygdrive::opendir () int fhandler_cygdrive::readdir (DIR *dir, dirent *de) { - if (!pdrive || !*pdrive) - return ENMFILE; - if (GetFileAttributes (pdrive) == INVALID_FILE_ATTRIBUTES) + while (true) { + if (!pdrive || !*pdrive) + return ENMFILE; + if (GetFileAttributes (pdrive) != INVALID_FILE_ATTRIBUTES) + break; pdrive = strchr (pdrive, '\0') + 1; - return readdir (dir, de); } - *de->d_name = cyg_tolower (*pdrive); de->d_name[1] = '\0'; + de->d_ino = readdir_get_ino (dir, pdrive, false); dir->__d_position++; pdrive = strchr (pdrive, '\0') + 1; syscall_printf ("%p = readdir (%p) (%s)", &de, dir, de->d_name); diff --git a/winsup/cygwin/fhandler_proc.cc b/winsup/cygwin/fhandler_proc.cc index 5194c9e82..1eebfe601 100644 --- a/winsup/cygwin/fhandler_proc.cc +++ b/winsup/cygwin/fhandler_proc.cc @@ -202,6 +202,7 @@ fhandler_proc::readdir (DIR *dir, dirent *de) if (dir->__d_position < PROC_LINK_COUNT) { strcpy (de->d_name, proc_listing[dir->__d_position++]); + dir->__flags |= dirent_saw_dot | dirent_saw_dot_dot; res = 0; } else diff --git a/winsup/cygwin/fhandler_process.cc b/winsup/cygwin/fhandler_process.cc index 648ced4ab..cb0b80520 100644 --- a/winsup/cygwin/fhandler_process.cc +++ b/winsup/cygwin/fhandler_process.cc @@ -1,6 +1,6 @@ /* fhandler_process.cc: fhandler for /proc/ virtual filesystem - Copyright 2002, 2003, 2004, 2005 Red Hat, Inc. + Copyright 2002, 2003, 2004, 2005, 2006 Red Hat, Inc. This file is part of Cygwin. @@ -227,6 +227,7 @@ fhandler_process::readdir (DIR *dir, dirent *de) } else strcpy (de->d_name, process_listing[dir->__d_position++]); + dir->__flags |= dirent_saw_dot | dirent_saw_dot_dot; res = 0; out: syscall_printf ("%d = readdir (%p, %p) (%s)", res, dir, de, de->d_name); diff --git a/winsup/cygwin/fhandler_registry.cc b/winsup/cygwin/fhandler_registry.cc index 7c253e03e..0982755d9 100644 --- a/winsup/cygwin/fhandler_registry.cc +++ b/winsup/cygwin/fhandler_registry.cc @@ -1,6 +1,6 @@ /* fhandler_registry.cc: fhandler for /proc/registry virtual filesystem - Copyright 2002, 2003, 2003, 2004, 2005 Red Hat, Inc. + Copyright 2002, 2003, 2003, 2004, 2005, 2006 Red Hat, Inc. This file is part of Cygwin. @@ -283,6 +283,7 @@ fhandler_registry::readdir (DIR *dir, dirent *de) LONG error; int res = ENMFILE; + dir->__flags |= dirent_saw_dot | dirent_saw_dot_dot; if (*path == 0) { if (dir->__d_position >= ROOT_KEY_COUNT) diff --git a/winsup/cygwin/include/sys/dirent.h b/winsup/cygwin/include/sys/dirent.h index 511fbb373..a12fefe36 100644 --- a/winsup/cygwin/include/sys/dirent.h +++ b/winsup/cygwin/include/sys/dirent.h @@ -1,6 +1,6 @@ /* Posix dirent.h for WIN32. - Copyright 2001, 2002, 2003, 2005 Red Hat, Inc. + Copyright 2001, 2002, 2003, 2005, 2006 Red Hat, Inc. This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for @@ -20,9 +20,9 @@ struct dirent { long __d_version; /* Used internally */ - __ino64_t __dirent_internal; - __uint32_t __dirent_unused1; - __uint32_t __dirent_internal1; + __ino64_t d_ino; + __uint32_t __d_fd; + __uint32_t __d_internal1; char d_name[256]; /* FIXME: use NAME_MAX? */ }; #else @@ -31,7 +31,7 @@ struct dirent long d_version; long d_reserved[2]; long d_fd; - ino_t __invalid_d_ino; /* DO NOT USE: No longer available since cygwin 1.5.19 */ + ino_t d_ino; char d_name[256]; }; #endif @@ -55,14 +55,7 @@ typedef struct __DIR } DIR; #pragma pack(pop) -#ifndef __USE_EXPENSIVE_CYGWIN_D_INO DIR *opendir (const char *); -#else -#define d_ino __dirent_internal -DIR *__opendir_with_d_ino (const char *); -#define opendir __opendir_with_d_ino -#endif - struct dirent *readdir (DIR *); int readdir_r (DIR *, struct dirent *, struct dirent **); void rewinddir (DIR *); diff --git a/winsup/cygwin/ntdll.h b/winsup/cygwin/ntdll.h index 2816354e0..1e1bd1145 100644 --- a/winsup/cygwin/ntdll.h +++ b/winsup/cygwin/ntdll.h @@ -1,6 +1,6 @@ /* ntdll.h. Contains ntdll specific stuff not defined elsewhere. - Copyright 2000, 2001, 2002, 2003, 2004 Red Hat, Inc. + Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006 Red Hat, Inc. This file is part of Cygwin. @@ -9,8 +9,10 @@ details. */ #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS) 0xc0000004) +#define STATUS_INVALID_PARAMETER ((NTSTATUS) 0xc000000d) #define STATUS_BUFFER_TOO_SMALL ((NTSTATUS) 0xc0000023) #define STATUS_WORKING_SET_QUOTA ((NTSTATUS) 0xc00000a1L) +#define STATUS_INVALID_LEVEL ((NTSTATUS) 0xc0000148) #define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L) #define PDI_MODULES 0x01 #define PDI_HEAPS 0x04 @@ -460,7 +462,7 @@ typedef struct _FILE_STANDARD_INFORMATION { } FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION; typedef struct _FILE_INTERNAL_INFORMATION { - LARGE_INTEGER IndexNumber; + LARGE_INTEGER FileId; } FILE_INTERNAL_INFORMATION, *PFILE_INTERNAL_INFORMATION; typedef struct _FILE_EA_INFORMATION { diff --git a/winsup/cygwin/path.h b/winsup/cygwin/path.h index 27c21496f..6bd18742f 100644 --- a/winsup/cygwin/path.h +++ b/winsup/cygwin/path.h @@ -1,6 +1,7 @@ /* path.h: path data structures - Copyright 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. + Copyright 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2004, 2005, + 2006 Red Hat, Inc. This file is part of Cygwin. @@ -135,7 +136,7 @@ class path_conv bool isremote () {return fs.is_remote_drive ();} int has_acls () const {return fs.has_acls (); } int has_symlinks () const {return path_flags & PATH_HAS_SYMLINKS;} - int hasgood_inode () const {return has_acls ();} // Not strictly correct + bool hasgood_inode (); /* Implemented in fhandler_disk_file.cc */ int has_buggy_open () const {return fs.has_buggy_open ();} bool isencoded () {return path_flags & PATH_ENC;} int binmode () const diff --git a/winsup/cygwin/wincap.cc b/winsup/cygwin/wincap.cc index bd9fd780d..9ff3c92f1 100644 --- a/winsup/cygwin/wincap.cc +++ b/winsup/cygwin/wincap.cc @@ -63,7 +63,8 @@ static NO_COPY wincaps wincap_unknown = { has_null_console_handler_routine:false, has_disk_ex_ioctls:false, has_working_virtual_lock:false, - has_disabled_user_tos_setting:false + has_disabled_user_tos_setting:false, + has_fileid_dirinfo:false }; static NO_COPY wincaps wincap_95 = { @@ -118,7 +119,8 @@ static NO_COPY wincaps wincap_95 = { has_null_console_handler_routine:false, has_disk_ex_ioctls:false, has_working_virtual_lock:false, - has_disabled_user_tos_setting:false + has_disabled_user_tos_setting:false, + has_fileid_dirinfo:false }; static NO_COPY wincaps wincap_95osr2 = { @@ -173,7 +175,8 @@ static NO_COPY wincaps wincap_95osr2 = { has_null_console_handler_routine:false, has_disk_ex_ioctls:false, has_working_virtual_lock:false, - has_disabled_user_tos_setting:false + has_disabled_user_tos_setting:false, + has_fileid_dirinfo:false }; static NO_COPY wincaps wincap_98 = { @@ -228,7 +231,8 @@ static NO_COPY wincaps wincap_98 = { has_null_console_handler_routine:false, has_disk_ex_ioctls:false, has_working_virtual_lock:false, - has_disabled_user_tos_setting:false + has_disabled_user_tos_setting:false, + has_fileid_dirinfo:false }; static NO_COPY wincaps wincap_98se = { @@ -283,7 +287,8 @@ static NO_COPY wincaps wincap_98se = { has_null_console_handler_routine:false, has_disk_ex_ioctls:false, has_working_virtual_lock:false, - has_disabled_user_tos_setting:false + has_disabled_user_tos_setting:false, + has_fileid_dirinfo:false }; static NO_COPY wincaps wincap_me = { @@ -338,7 +343,8 @@ static NO_COPY wincaps wincap_me = { has_null_console_handler_routine:false, has_disk_ex_ioctls:false, has_working_virtual_lock:false, - has_disabled_user_tos_setting:false + has_disabled_user_tos_setting:false, + has_fileid_dirinfo:false }; static NO_COPY wincaps wincap_nt3 = { @@ -393,7 +399,8 @@ static NO_COPY wincaps wincap_nt3 = { has_null_console_handler_routine:true, has_disk_ex_ioctls:false, has_working_virtual_lock:true, - has_disabled_user_tos_setting:false + has_disabled_user_tos_setting:false, + has_fileid_dirinfo:false }; static NO_COPY wincaps wincap_nt4 = { @@ -448,7 +455,8 @@ static NO_COPY wincaps wincap_nt4 = { has_null_console_handler_routine:true, has_disk_ex_ioctls:false, has_working_virtual_lock:true, - has_disabled_user_tos_setting:false + has_disabled_user_tos_setting:false, + has_fileid_dirinfo:false }; static NO_COPY wincaps wincap_nt4sp4 = { @@ -503,7 +511,8 @@ static NO_COPY wincaps wincap_nt4sp4 = { has_null_console_handler_routine:true, has_disk_ex_ioctls:false, has_working_virtual_lock:true, - has_disabled_user_tos_setting:false + has_disabled_user_tos_setting:false, + has_fileid_dirinfo:false }; static NO_COPY wincaps wincap_2000 = { @@ -558,7 +567,8 @@ static NO_COPY wincaps wincap_2000 = { has_null_console_handler_routine:true, has_disk_ex_ioctls:false, has_working_virtual_lock:true, - has_disabled_user_tos_setting:true + has_disabled_user_tos_setting:true, + has_fileid_dirinfo:true }; static NO_COPY wincaps wincap_xp = { @@ -613,7 +623,8 @@ static NO_COPY wincaps wincap_xp = { has_null_console_handler_routine:true, has_disk_ex_ioctls:true, has_working_virtual_lock:true, - has_disabled_user_tos_setting:true + has_disabled_user_tos_setting:true, + has_fileid_dirinfo:true }; static NO_COPY wincaps wincap_2003 = { @@ -668,7 +679,8 @@ static NO_COPY wincaps wincap_2003 = { has_null_console_handler_routine:true, has_disk_ex_ioctls:true, has_working_virtual_lock:true, - has_disabled_user_tos_setting:true + has_disabled_user_tos_setting:true, + has_fileid_dirinfo:true }; static NO_COPY wincaps wincap_vista = { @@ -723,7 +735,8 @@ static NO_COPY wincaps wincap_vista = { has_null_console_handler_routine:true, has_disk_ex_ioctls:true, has_working_virtual_lock:true, - has_disabled_user_tos_setting:true + has_disabled_user_tos_setting:true, + has_fileid_dirinfo:true }; wincapc wincap __attribute__((section (".cygwin_dll_common"), shared)); diff --git a/winsup/cygwin/wincap.h b/winsup/cygwin/wincap.h index f14b274dd..5738850e0 100644 --- a/winsup/cygwin/wincap.h +++ b/winsup/cygwin/wincap.h @@ -65,6 +65,7 @@ struct wincaps unsigned has_disk_ex_ioctls : 1; unsigned has_working_virtual_lock : 1; unsigned has_disabled_user_tos_setting : 1; + unsigned has_fileid_dirinfo : 1; }; class wincapc @@ -136,6 +137,7 @@ public: bool IMPLEMENT (has_disk_ex_ioctls) bool IMPLEMENT (has_working_virtual_lock) bool IMPLEMENT (has_disabled_user_tos_setting) + bool IMPLEMENT (has_fileid_dirinfo) #undef IMPLEMENT }; diff --git a/winsup/cygwin/winsup.h b/winsup/cygwin/winsup.h index 55031f9b9..c0059ed44 100644 --- a/winsup/cygwin/winsup.h +++ b/winsup/cygwin/winsup.h @@ -1,7 +1,7 @@ /* winsup.h: main Cygwin header file. Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, - 2005 Red Hat, Inc. + 2005, 2006 Red Hat, Inc. This file is part of Cygwin. @@ -293,6 +293,8 @@ class path_conv; int fcntl_worker (int fd, int cmd, void *arg); +__ino64_t __stdcall readdir_get_ino (struct __DIR *dir, const char *path, bool dot_dot) __attribute__ ((regparm (3))); + extern "C" int low_priority_sleep (DWORD) __attribute__ ((regparm (1))); #define SLEEP_0_STAY_LOW INFINITE