1 /* dir.cc: Posix directory-related routines
3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
5 This file is part of Cygwin.
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
17 #define _COMPILING_NEWLIB
29 /* Return whether the directory of a file is writable. Return 1 if it
30 is. Otherwise, return 0, and set errno appropriately. */
32 writable_directory (const char *file)
35 char dir[strlen (file) + 1];
40 char *slash = strrchr (dir, '\\');
43 else if (slash == dir)
53 int acc = access (usedir, W_OK);
64 if (check_null_invalid_struct_errno (dir))
66 if (dir->__d_cookie != __DIRENT_COOKIE)
69 syscall_printf ("-1 = dirfd (%p)", dir);
72 return dir->__d_dirent->d_fd;
75 /* opendir: POSIX 5.1.2.1 */
77 opendir (const char *name)
83 fh = cygheap->fdtab.build_fhandler_from_name (-1, name, NULL, pc,
84 PC_SYM_FOLLOW | PC_FULL, NULL);
87 else if (pc.exists ())
88 res = fh->opendir (pc);
100 /* readdir: POSIX 5.1.2.1 */
101 extern "C" struct dirent *
104 if (check_null_invalid_struct_errno (dir))
107 if (dir->__d_cookie != __DIRENT_COOKIE)
110 syscall_printf ("%p = readdir (%p)", NULL, dir);
114 dirent *res = ((fhandler_base *) dir->__d_u.__d_data.__fh)->readdir (dir);
118 /* Compute d_ino by combining filename hash with the directory hash
119 (which was stored in dir->__d_dirhash when opendir was called). */
120 if (res->d_name[0] == '.')
122 if (res->d_name[1] == '\0')
123 dir->__d_dirent->d_ino = dir->__d_dirhash;
124 else if (res->d_name[1] != '.' || res->d_name[2] != '\0')
128 char *p, up[strlen (dir->__d_dirname) + 1];
129 strcpy (up, dir->__d_dirname);
130 if (!(p = strrchr (up, '\\')))
133 if (!(p = strrchr (up, '\\')))
134 dir->__d_dirent->d_ino = hash_path_name (0, ".");
138 dir->__d_dirent->d_ino = hash_path_name (0, up);
145 ino_t dino = hash_path_name (dir->__d_dirhash, "\\");
146 dir->__d_dirent->d_ino = hash_path_name (dino, res->d_name);
155 if (check_null_invalid_struct_errno (dir))
158 if (dir->__d_cookie != __DIRENT_COOKIE)
160 return ((fhandler_base *) dir->__d_u.__d_data.__fh)->telldir (dir);
167 return telldir64 (dir);
171 seekdir64 (DIR *dir, __off64_t loc)
173 if (check_null_invalid_struct_errno (dir))
176 if (dir->__d_cookie != __DIRENT_COOKIE)
178 return ((fhandler_base *) dir->__d_u.__d_data.__fh)->seekdir (dir, loc);
183 seekdir (DIR *dir, __off32_t loc)
185 seekdir64 (dir, (__off64_t)loc);
188 /* rewinddir: POSIX 5.1.2.1 */
192 if (check_null_invalid_struct_errno (dir))
195 if (dir->__d_cookie != __DIRENT_COOKIE)
197 return ((fhandler_base *) dir->__d_u.__d_data.__fh)->rewinddir (dir);
200 /* closedir: POSIX 5.1.2.1 */
204 if (check_null_invalid_struct_errno (dir))
207 if (dir->__d_cookie != __DIRENT_COOKIE)
210 syscall_printf ("-1 = closedir (%p)", dir);
214 /* Reset the marker in case the caller tries to use `dir' again. */
217 int res = ((fhandler_base *) dir->__d_u.__d_data.__fh)->closedir (dir);
219 cygheap->fdtab.release (dir->__d_dirent->d_fd);
221 free (dir->__d_dirname);
222 free (dir->__d_dirent);
224 syscall_printf ("%d = closedir (%p)", res);
228 /* mkdir: POSIX 5.4.1.1 */
230 mkdir (const char *dir, mode_t mode)
233 SECURITY_ATTRIBUTES sa = sec_none_nih;
235 path_conv real_dir (dir, PC_SYM_NOFOLLOW);
239 set_errno (real_dir.case_clash ? ECASECLASH : real_dir.error);
243 nofinalslash (real_dir.get_win32 (), real_dir.get_win32 ());
244 if (! writable_directory (real_dir.get_win32 ()))
247 if (allow_ntsec && real_dir.has_acls ())
248 set_security_attribute (S_IFDIR | ((mode & 07777) & ~cygheap->umask),
249 &sa, alloca (4096), 4096);
251 if (CreateDirectoryA (real_dir.get_win32 (), &sa))
253 if (!allow_ntsec && allow_ntea)
254 set_file_attribute (real_dir.has_acls (), real_dir.get_win32 (),
255 S_IFDIR | ((mode & 07777) & ~cygheap->umask));
256 #ifdef HIDDEN_DOT_FILES
257 char *c = strrchr (real_dir.get_win32 (), '\\');
258 if ((c && c[1] == '.') || *real_dir.get_win32 () == '.')
259 SetFileAttributes (real_dir.get_win32 (), FILE_ATTRIBUTE_HIDDEN);
267 syscall_printf ("%d = mkdir (%s, %d)", res, dir, mode);
271 /* rmdir: POSIX 5.5.2.1 */
273 rmdir (const char *dir)
278 path_conv real_dir (dir, PC_SYM_NOFOLLOW);
281 set_errno (real_dir.error);
282 else if ((devn = real_dir.get_devn ()) == FH_PROC || devn == FH_REGISTRY
283 || devn == FH_PROCESS)
285 else if (!real_dir.exists ())
287 else if (!real_dir.isdir ())
291 /* Even own directories can't be removed if R/O attribute is set. */
292 if (real_dir.has_attribute (FILE_ATTRIBUTE_READONLY))
293 SetFileAttributes (real_dir,
294 (DWORD) real_dir & ~FILE_ATTRIBUTE_READONLY);
296 if (RemoveDirectory (real_dir))
298 /* RemoveDirectory on a samba drive doesn't return an error if the
299 directory can't be removed because it's not empty. Checking for
300 existence afterwards keeps us informed about success. */
301 if (GetFileAttributes (real_dir) != INVALID_FILE_ATTRIBUTES)
302 set_errno (ENOTEMPTY);
308 /* This kludge detects if we are attempting to remove the current working
309 directory. If so, we will move elsewhere to potentially allow the
310 rmdir to succeed. This means that cygwin's concept of the current working
311 directory != Windows concept but, hey, whaddaregonnado?
312 Note that this will not cause something like the following to work:
315 since the shell will have foo "open" in the above case and so Windows will
316 not allow the deletion.
317 FIXME: A potential workaround for this is for cygwin apps to *never* call
318 SetCurrentDirectory. */
319 if (strcasematch (real_dir, cygheap->cwd.win32)
320 && !strcasematch ("c:\\", cygheap->cwd.win32))
322 DWORD err = GetLastError ();
323 if (!SetCurrentDirectory ("c:\\"))
325 else if ((res = rmdir (dir)))
326 SetCurrentDirectory (cygheap->cwd.win32);
330 if (GetLastError () != ERROR_ACCESS_DENIED
331 || !wincap.access_denied_on_delete ())
334 set_errno (ENOTEMPTY); /* On 9X ERROR_ACCESS_DENIED is
335 returned if you try to remove a
336 non-empty directory. */
338 /* If directory still exists, restore R/O attribute. */
339 if (real_dir.has_attribute (FILE_ATTRIBUTE_READONLY))
340 SetFileAttributes (real_dir, real_dir);
345 syscall_printf ("%d = rmdir (%s)", res, dir);