/* dir.cc: Posix directory-related routines
- Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2006, 2007,
+ 2008, 2009, 2010 Red Hat, Inc.
This file is part of Cygwin.
details. */
#include "winsup.h"
-#include <unistd.h>
#include <stdlib.h>
-#include <sys/stat.h>
#define _COMPILING_NEWLIB
#include <dirent.h>
-#include "pinfo.h"
#include "cygerrno.h"
#include "security.h"
#include "path.h"
#include "fhandler.h"
#include "dtable.h"
#include "cygheap.h"
+#include "cygtls.h"
+#include "tls_pbuf.h"
extern "C" int
dirfd (DIR *dir)
{
- if (check_null_invalid_struct_errno (dir))
+ myfault efault;
+ if (efault.faulted (EFAULT))
return -1;
if (dir->__d_cookie != __DIRENT_COOKIE)
{
syscall_printf ("-1 = dirfd (%p)", dir);
return -1;
}
- return dir->__d_dirent->d_fd;
+ 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)
+{
+ return opendir (name);
}
/* opendir: POSIX 5.1.2.1 */
fhandler_base *fh;
DIR *res;
- fh = build_fh_name (name, NULL, PC_SYM_FOLLOW);
+ fh = build_fh_name (name, PC_SYM_FOLLOW);
if (!fh)
res = NULL;
else if (fh->exists ())
- res = fh->opendir ();
+ res = fh->opendir (-1);
else
{
set_errno (ENOENT);
res = NULL;
}
- if (res)
- res->__flags = 0;
- else if (fh)
+ if (!res && fh)
delete fh;
return res;
}
-/* readdir: POSIX 5.1.2.1 */
-extern "C" struct dirent *
-readdir (DIR *dir)
+extern "C" DIR *
+fdopendir (int fd)
{
- if (check_null_invalid_struct_errno (dir))
- return NULL;
+ DIR *res = NULL;
+
+ cygheap_fdget cfd (fd);
+ if (cfd >= 0)
+ res = cfd->opendir (fd);
+ return res;
+}
+
+static int
+readdir_worker (DIR *dir, dirent *de)
+{
+ myfault efault;
+ if (efault.faulted ())
+ return EFAULT;
if (dir->__d_cookie != __DIRENT_COOKIE)
{
- set_errno (EBADF);
syscall_printf ("%p = readdir (%p)", NULL, dir);
- return NULL;
+ return EBADF;
}
- dirent *res = ((fhandler_base *) dir->__fh)->readdir (dir);
+ de->d_ino = 0;
+ de->d_type = DT_UNKNOWN;
+ memset (&de->__d_unused1, 0, sizeof (de->__d_unused1));
- if (!res)
+ int res = ((fhandler_base *) dir->__fh)->readdir (dir, de);
+
+ if (res == ENMFILE)
{
if (!(dir->__flags & dirent_saw_dot))
{
- res = dir->__d_dirent;
- strcpy (res->d_name, ".");
+ strcpy (de->d_name, ".");
dir->__flags |= dirent_saw_dot;
dir->__d_position++;
+ res = 0;
}
else if (!(dir->__flags & dirent_saw_dot_dot))
{
- res = dir->__d_dirent;
- strcpy (res->d_name, "..");
+ strcpy (de->d_name, "..");
dir->__flags |= dirent_saw_dot_dot;
dir->__d_position++;
+ res = 0;
}
}
- 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 (res->d_name[0] == '.')
+ bool is_dot = false;
+ bool is_dot_dot = false;
+
+ if (de->d_name[0] == '.')
{
- if (res->d_name[1] == '\0')
- {
- dir->__d_dirent->d_ino = dir->__d_dirhash;
- dir->__flags |= dirent_saw_dot;
- }
- else if (res->d_name[1] != '.' || res->d_name[2] != '\0')
- goto hashit;
- else
- {
- dir->__flags |= dirent_saw_dot_dot;
- char *p, up[strlen (dir->__d_dirname) + 1];
- strcpy (up, dir->__d_dirname);
- if (!(p = strrchr (up, '\\')))
- goto hashit;
- *p = '\0';
- if (!(p = strrchr (up, '\\')))
- dir->__d_dirent->d_ino = hash_path_name (0, ".");
- else
- {
- *p = '\0';
- dir->__d_dirent->d_ino = hash_path_name (0, up);
- }
- }
+ 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 (((fhandler_base *) dir->__fh)->get_name (),
+ true);
else
{
- hashit:
- __ino64_t dino = hash_path_name (dir->__d_dirhash, "\\");
- dir->__d_dirent->d_ino = hash_path_name (dino, res->d_name);
+ /* Compute d_ino by combining filename hash with directory hash. */
+ de->d_ino = ((fhandler_base *) dir->__fh)->get_ino ();
+ if (!is_dot && !is_dot_dot)
+ {
+ PUNICODE_STRING w32name =
+ ((fhandler_base *) dir->__fh)->pc.get_nt_native_path ();
+ DWORD devn = ((fhandler_base *) dir->__fh)->get_device ();
+ /* Paths below /proc don't have a Win32 pendant. */
+ if (isproc_dev (devn))
+ de->d_ino = hash_path_name (de->d_ino, L"/");
+ else if (w32name->Buffer[w32name->Length / sizeof (WCHAR) - 1]
+ != L'\\')
+ de->d_ino = hash_path_name (de->d_ino, L"\\");
+ de->d_ino = hash_path_name (de->d_ino, de->d_name);
+ }
}
- res->__ino32 = dir->__d_dirent->d_ino; // for legacy applications
+ }
+ /* This fills out the old 32 bit d_ino field for old applications,
+ build under Cygwin before 1.5.x. */
+ de->__d_internal1 = de->d_ino;
+
+ return res;
+}
+
+/* readdir: POSIX 5.1.2.1 */
+extern "C" struct dirent *
+readdir (DIR *dir)
+{
+ int res = readdir_worker (dir, dir->__d_dirent);
+ if (res == 0)
+ return dir->__d_dirent;
+ if (res != ENMFILE)
+ set_errno (res);
+ return NULL;
+}
+
+extern "C" int
+readdir_r (DIR *dir, dirent *de, dirent **ode)
+{
+ int res = readdir_worker (dir, de);
+ if (!res)
+ *ode = de;
+ else
+ {
+ *ode = NULL;
+ if (res == ENMFILE)
+ res = 0;
}
return res;
}
extern "C" _off64_t
telldir64 (DIR *dir)
{
- if (check_null_invalid_struct_errno (dir))
+ myfault efault;
+ if (efault.faulted (EFAULT))
return -1;
if (dir->__d_cookie != __DIRENT_COOKIE)
extern "C" void
seekdir64 (DIR *dir, _off64_t loc)
{
- if (check_null_invalid_struct_errno (dir))
+ myfault efault;
+ if (efault.faulted (EFAULT))
return;
if (dir->__d_cookie != __DIRENT_COOKIE)
return;
- dir->__flags = 0;
+ dir->__flags &= dirent_info_mask;
return ((fhandler_base *) dir->__fh)->seekdir (dir, loc);
}
extern "C" void
rewinddir (DIR *dir)
{
- if (check_null_invalid_struct_errno (dir))
+ myfault efault;
+ if (efault.faulted (EFAULT))
return;
if (dir->__d_cookie != __DIRENT_COOKIE)
return;
- dir->__flags = 0;
+ dir->__flags &= dirent_info_mask;
return ((fhandler_base *) dir->__fh)->rewinddir (dir);
}
extern "C" int
closedir (DIR *dir)
{
- if (check_null_invalid_struct_errno (dir))
+ myfault efault;
+ if (efault.faulted (EFAULT))
return -1;
if (dir->__d_cookie != __DIRENT_COOKIE)
int res = ((fhandler_base *) dir->__fh)->closedir (dir);
- cygheap->fdtab.release (dir->__d_dirent->d_fd);
+ cygheap->fdtab.release (dir->__d_fd);
free (dir->__d_dirname);
free (dir->__d_dirent);
mkdir (const char *dir, mode_t mode)
{
int res = -1;
- SECURITY_ATTRIBUTES sa = sec_none_nih;
- security_descriptor sd;
+ fhandler_base *fh = NULL;
+ tmp_pathbuf tp;
+
+ myfault efault;
+ if (efault.faulted (EFAULT))
+ return -1;
- path_conv real_dir (dir, PC_SYM_NOFOLLOW | PC_WRITABLE);
+ /* POSIX says mkdir("symlink-to-missing/") should create the
+ directory "missing", but Linux rejects it with EEXIST. Copy
+ Linux behavior for now. */
- if (real_dir.error)
+ if (!*dir)
{
- set_errno (real_dir.case_clash ? ECASECLASH : real_dir.error);
+ set_errno (ENOENT);
goto done;
}
+ if (isdirsep (dir[strlen (dir) - 1]))
+ {
+ /* This converts // to /, but since both give EEXIST, we're okay. */
+ char *buf;
+ char *p = stpcpy (buf = tp.c_get (), dir) - 1;
+ dir = buf;
+ while (p > dir && isdirsep (*p))
+ *p-- = '\0';
+ }
+ if (!(fh = build_fh_name (dir, PC_SYM_NOFOLLOW)))
+ goto done; /* errno already set */;
- nofinalslash (real_dir.get_win32 (), real_dir.get_win32 ());
-
- if (allow_ntsec && real_dir.has_acls ())
- set_security_attribute (S_IFDIR | ((mode & 07777) & ~cygheap->umask),
- &sa, sd);
-
- if (CreateDirectoryA (real_dir.get_win32 (), &sa))
+ if (fh->error ())
{
- if (!allow_ntsec && allow_ntea)
- set_file_attribute (false, NULL, real_dir.get_win32 (),
- S_IFDIR | ((mode & 07777) & ~cygheap->umask));
-#ifdef HIDDEN_DOT_FILES
- char *c = strrchr (real_dir.get_win32 (), '\\');
- if ((c && c[1] == '.') || *real_dir.get_win32 () == '.')
- SetFileAttributes (real_dir.get_win32 (), FILE_ATTRIBUTE_HIDDEN);
-#endif
- res = 0;
+ debug_printf ("got %d error from build_fh_name", fh->error ());
+ set_errno (fh->error ());
}
- else
- __seterrno ();
+ else if (has_dot_last_component (dir, true))
+ set_errno (fh->exists () ? EEXIST : ENOENT);
+ else if (!fh->mkdir (mode))
+ res = 0;
+ delete fh;
-done:
+ done:
syscall_printf ("%d = mkdir (%s, %d)", res, dir, mode);
return res;
}
rmdir (const char *dir)
{
int res = -1;
+ fhandler_base *fh = NULL;
- path_conv real_dir (dir, PC_SYM_NOFOLLOW | PC_WRITABLE);
+ myfault efault;
+ if (efault.faulted (EFAULT))
+ return -1;
- if (real_dir.error)
- set_errno (real_dir.error);
- else if (!real_dir.exists ())
- set_errno (ENOENT);
- else if (!real_dir.isdir ())
- set_errno (ENOTDIR);
- else
- {
- /* Even own directories can't be removed if R/O attribute is set. */
- if (real_dir.has_attribute (FILE_ATTRIBUTE_READONLY))
- SetFileAttributes (real_dir,
- (DWORD) real_dir & ~FILE_ATTRIBUTE_READONLY);
+ if (!(fh = build_fh_name (dir, PC_SYM_NOFOLLOW)))
+ goto done; /* errno already set */;
- for (bool is_cwd = false; ; is_cwd = true)
- {
- DWORD err;
- int rc = RemoveDirectory (real_dir);
- DWORD att = GetFileAttributes (real_dir);
-
- /* Sometimes smb indicates failure when it really succeeds, so check for
- this case specifically. */
- if (rc || att == INVALID_FILE_ATTRIBUTES)
- {
- /* RemoveDirectory on a samba drive doesn't return an error if the
- directory can't be removed because it's not empty. Checking for
- existence afterwards keeps us informed about success. */
- if (att == INVALID_FILE_ATTRIBUTES)
- {
- res = 0;
- break;
- }
- err = ERROR_DIR_NOT_EMPTY;
- }
- else
- err = GetLastError ();
-
- /* This kludge detects if we are attempting to remove the current working
- directory. If so, we will move elsewhere to potentially allow the
- rmdir to succeed. This means that cygwin's concept of the current working
- directory != Windows concept but, hey, whaddaregonnado?
- Note that this will not cause something like the following to work:
- $ cd foo
- $ rmdir .
- since the shell will have foo "open" in the above case and so Windows will
- not allow the deletion. (Actually it does on 9X.)
- FIXME: A potential workaround for this is for cygwin apps to *never* call
- SetCurrentDirectory. */
-
- if (strcasematch (real_dir, cygheap->cwd.win32)
- && !strcasematch ("c:\\", cygheap->cwd.win32)
- && !is_cwd
- && SetCurrentDirectory ("c:\\"))
- continue;
-
- /* On 9X ERROR_ACCESS_DENIED is returned
- if you try to remove a non-empty directory. */
- if (err == ERROR_ACCESS_DENIED
- && wincap.access_denied_on_delete ())
- err = ERROR_DIR_NOT_EMPTY;
-
- __seterrno_from_win_error (err);
-
- /* Directory still exists, restore its characteristics. */
- if (real_dir.has_attribute (FILE_ATTRIBUTE_READONLY))
- SetFileAttributes (real_dir, real_dir);
- if (is_cwd)
- SetCurrentDirectory (real_dir);
- break;
- }
+ if (fh->error ())
+ {
+ debug_printf ("got %d error from build_fh_name", fh->error ());
+ set_errno (fh->error ());
}
+ else if (!fh->exists ())
+ set_errno (ENOENT);
+ else if (has_dot_last_component (dir, false))
+ set_errno (EINVAL);
+ else if (!fh->rmdir ())
+ res = 0;
+
+ delete fh;
+ done:
syscall_printf ("%d = rmdir (%s)", res, dir);
return res;
}