1 /* dir.cc: Posix directory-related routines
3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
31 if (dir
->__d_cookie
== __DIRENT_COOKIE
)
33 syscall_printf ("-1 = dirfd (%p)", dir
);
41 /* Symbol kept for backward compatibility. Don't remove. Don't declare
42 in public header file. */
44 __opendir_with_d_ino (const char *name
)
46 return opendir (name
);
49 /* opendir: POSIX 5.1.2.1 */
51 opendir (const char *name
)
56 fh
= build_fh_name (name
, PC_SYM_FOLLOW
);
59 else if (fh
->error ())
61 set_errno (fh
->error ());
64 else if (!fh
->exists ())
69 else if (!fh
->pc
.isdir ())
75 res
= fh
->opendir (-1);
79 /* Applications calling flock(2) on dirfd(fd) need this... */
80 if (res
&& !fh
->nohandle ())
90 cygheap_fdget
cfd (fd
);
92 res
= cfd
->opendir (fd
);
97 readdir_worker (DIR *dir
, dirent
*de
)
103 if (dir
->__d_cookie
!= __DIRENT_COOKIE
)
105 syscall_printf ("%p = readdir (%p)", NULL
, dir
);
111 de
->d_type
= DT_UNKNOWN
;
112 de
->d_reclen
= sizeof *de
;
113 memset (&de
->__d_unused1
, 0, sizeof (de
->__d_unused1
));
115 res
= ((fhandler_base
*) dir
->__fh
)->readdir (dir
, de
);
119 if (!(dir
->__flags
& dirent_saw_dot
))
121 strcpy (de
->d_name
, ".");
122 dir
->__flags
|= dirent_saw_dot
;
126 else if (!(dir
->__flags
& dirent_saw_dot_dot
))
128 strcpy (de
->d_name
, "..");
129 dir
->__flags
|= dirent_saw_dot_dot
;
135 if (!res
&& !de
->d_ino
)
138 bool is_dot_dot
= false;
140 if (de
->d_name
[0] == '.')
142 if (de
->d_name
[1] == '\0')
144 else if (de
->d_name
[1] == '.' && de
->d_name
[2] == '\0')
148 if (is_dot_dot
&& !(dir
->__flags
& dirent_isroot
))
149 de
->d_ino
= readdir_get_ino (((fhandler_base
*)
150 dir
->__fh
)->get_name (),
154 /* Compute d_ino by combining filename hash with directory hash. */
155 de
->d_ino
= ((fhandler_base
*) dir
->__fh
)->get_ino ();
156 if (!is_dot
&& !is_dot_dot
)
158 PUNICODE_STRING w32name
=
159 ((fhandler_base
*) dir
->__fh
)->pc
.get_nt_native_path ();
160 DWORD devn
= ((fhandler_base
*) dir
->__fh
)->get_device ();
161 /* Paths below /proc don't have a Win32 pendant. */
162 if (isproc_dev (devn
))
163 de
->d_ino
= hash_path_name (de
->d_ino
, L
"/");
164 else if (w32name
->Buffer
[w32name
->Length
/ sizeof (WCHAR
) - 1]
166 de
->d_ino
= hash_path_name (de
->d_ino
, L
"\\");
167 de
->d_ino
= hash_path_name (de
->d_ino
, de
->d_name
);
180 /* readdir: POSIX 5.1.2.1 */
181 extern "C" struct dirent
*
184 int res
= readdir_worker (dir
, dir
->__d_dirent
);
186 return dir
->__d_dirent
;
193 readdir_r (DIR *__restrict dir
, dirent
*__restrict de
, dirent
**__restrict ode
)
195 int res
= readdir_worker (dir
, de
);
208 ssize_t
posix_getdents(int fd
, void *buf
, size_t nbytes
, int flags
)
210 struct posix_dent
*dent_buf
, *src
;
213 cygheap_fdget
cfd (fd
);
214 /* Valid descriptor? */
217 /* Valid flags? Right now only DT_FORCE_TYPE is defined */
218 if ((flags
& ~DT_FORCE_TYPE
) != 0)
223 /* Create type-safe buffer pointer */
224 dent_buf
= (struct posix_dent
*) buf
;
225 /* Check if nbytes is big enough to fit at least one struct posix_dent */
226 if (nbytes
< sizeof (struct posix_dent
))
231 /* Create internal DIR * on first invocation */
232 if (!cfd
->getdents_dir ())
234 cfd
->getdents_dir (cfd
->opendir (fd
));
235 if (!cfd
->getdents_dir ())
238 /* Now loop until EOF or buf is full */
239 while (nbytes
>= sizeof (struct posix_dent
))
241 /* Our struct posix_dent is identical to struct dirent */
242 src
= (struct posix_dent
*) readdir (cfd
->getdents_dir ());
245 /* Handle the suggested DT_FORCE_TYPE flag */
246 if (src
->d_type
== DT_UNKNOWN
&& (flags
& DT_FORCE_TYPE
))
250 if (!fstatat (fd
, src
->d_name
, &st
, AT_SYMLINK_NOFOLLOW
))
251 src
->d_type
= IFTODT (st
.st_mode
);
255 nbytes
-= sizeof (struct posix_dent
);
257 return cnt
* sizeof (struct posix_dent
);
266 if (dir
->__d_cookie
== __DIRENT_COOKIE
)
267 return ((fhandler_base
*) dir
->__fh
)->telldir (dir
);
275 /* telldir was never defined using off_t in POSIX, only in early versions
276 of glibc. We have to keep the function in as entry point for backward
281 return (off_t
) telldir (dir
);
286 seekdir (DIR *dir
, long loc
)
290 if (dir
->__d_cookie
== __DIRENT_COOKIE
)
292 dir
->__flags
&= dirent_info_mask
;
293 ((fhandler_base
*) dir
->__fh
)->seekdir (dir
, loc
);
300 /* seekdir was never defined using off_t in POSIX, only in early versions
301 of glibc. We have to keep the function in as entry point for backward
304 seekdir64 (DIR *dir
, off_t loc
)
306 seekdir (dir
, (long) loc
);
309 /* rewinddir: POSIX 5.1.2.1 */
315 if (dir
->__d_cookie
== __DIRENT_COOKIE
)
317 dir
->__flags
&= dirent_info_mask
;
318 ((fhandler_base
*) dir
->__fh
)->rewinddir (dir
);
320 set_errno (EINVAL
); /* Diagnosis */
327 closedir_worker (DIR *dir
, int *fdret
)
331 if (dir
->__d_cookie
== __DIRENT_COOKIE
)
333 /* Reset the marker in case the caller tries to use `dir' again. */
336 int res
= ((fhandler_base
*) dir
->__fh
)->closedir (dir
);
339 *fdret
= dir
->__d_fd
;
343 free (dir
->__d_dirname
);
344 free (dir
->__d_dirent
);
355 /* closedir: POSIX 5.1.2.1 */
361 res
= closedir_worker (dir
, NULL
);
362 syscall_printf ("%R = closedir(%p)", res
, dir
);
367 fdclosedir (DIR *dir
)
371 closedir_worker (dir
, &fd
);
372 syscall_printf ("%d = fdclosedir(%p)", fd
, dir
);
376 /* mkdir: POSIX 5.4.1.1 */
378 mkdir (const char *dir
, mode_t mode
)
381 fhandler_base
*fh
= NULL
;
391 /* Following Linux, and intentionally ignoring POSIX, do not
392 resolve the last component of DIR if it is a symlink, even if
393 DIR has a trailing slash. Achieve this by stripping trailing
394 slashes or backslashes.
396 Exception: If DIR == 'x:' followed by one or more slashes or
397 backslashes, and if there's at least one backslash, assume
398 that the user is referring to the root directory of drive x.
399 Retain one backslash in this case. */
400 if (isdirsep (dir
[strlen (dir
) - 1]))
402 /* This converts // to /, but since both give EEXIST, we're okay. */
404 char *p
= stpcpy (buf
= tp
.c_get (), dir
) - 1;
407 while (p
> dir
&& isdirsep (*p
))
413 if (msdos
&& p
== dir
+ 1 && isdrive (dir
))
416 if (!(fh
= build_fh_name (dir
, PC_SYM_NOFOLLOW
)))
417 __leave
; /* errno already set */;
421 debug_printf ("got %d error from build_fh_name", fh
->error ());
422 set_errno (fh
->error ());
424 else if (fh
->exists ())
426 else if (has_dot_last_component (dir
, true))
428 else if (!fh
->mkdir (mode
))
434 syscall_printf ("%R = mkdir(%s, %d)", res
, dir
, mode
);
438 /* rmdir: POSIX 5.5.2.1 */
440 rmdir (const char *dir
)
443 fhandler_base
*fh
= NULL
;
453 /* Following Linux, and intentionally ignoring POSIX, do not
454 resolve the last component of DIR if it is a symlink, even if
455 DIR has a trailing slash. Achieve this by stripping trailing
456 slashes or backslashes.
458 Exception: If DIR == 'x:' followed by one or more slashes or
459 backslashes, and if there's at least one backslash, assume
460 that the user is referring to the root directory of drive x.
461 Retain one backslash in this case. */
462 if (isdirsep (dir
[strlen (dir
) - 1]))
464 /* This converts // to /, but since both give ENOTEMPTY,
467 char *p
= stpcpy (buf
= tp
.c_get (), dir
) - 1;
470 while (p
> dir
&& isdirsep (*p
))
476 if (msdos
&& p
== dir
+ 1 && isdrive (dir
))
479 if (!(fh
= build_fh_name (dir
, PC_SYM_NOFOLLOW
)))
480 __leave
; /* errno already set */;
484 debug_printf ("got %d error from build_fh_name", fh
->error ());
485 set_errno (fh
->error ());
487 else if (!fh
->exists ())
489 else if (has_dot_last_component (dir
, false))
491 else if (!fh
->rmdir ())
497 syscall_printf ("%R = rmdir(%s)", res
, dir
);