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 ())
65 res
= fh
->opendir (-1);
74 /* Applications calling flock(2) on dirfd(fd) need this... */
75 if (res
&& !fh
->nohandle ())
85 cygheap_fdget
cfd (fd
);
87 res
= cfd
->opendir (fd
);
92 readdir_worker (DIR *dir
, dirent
*de
)
98 if (dir
->__d_cookie
!= __DIRENT_COOKIE
)
100 syscall_printf ("%p = readdir (%p)", NULL
, dir
);
106 de
->d_type
= DT_UNKNOWN
;
107 de
->d_reclen
= sizeof *de
;
108 memset (&de
->__d_unused1
, 0, sizeof (de
->__d_unused1
));
110 res
= ((fhandler_base
*) dir
->__fh
)->readdir (dir
, de
);
114 if (!(dir
->__flags
& dirent_saw_dot
))
116 strcpy (de
->d_name
, ".");
117 dir
->__flags
|= dirent_saw_dot
;
121 else if (!(dir
->__flags
& dirent_saw_dot_dot
))
123 strcpy (de
->d_name
, "..");
124 dir
->__flags
|= dirent_saw_dot_dot
;
130 if (!res
&& !de
->d_ino
)
133 bool is_dot_dot
= false;
135 if (de
->d_name
[0] == '.')
137 if (de
->d_name
[1] == '\0')
139 else if (de
->d_name
[1] == '.' && de
->d_name
[2] == '\0')
143 if (is_dot_dot
&& !(dir
->__flags
& dirent_isroot
))
144 de
->d_ino
= readdir_get_ino (((fhandler_base
*)
145 dir
->__fh
)->get_name (),
149 /* Compute d_ino by combining filename hash with directory hash. */
150 de
->d_ino
= ((fhandler_base
*) dir
->__fh
)->get_ino ();
151 if (!is_dot
&& !is_dot_dot
)
153 PUNICODE_STRING w32name
=
154 ((fhandler_base
*) dir
->__fh
)->pc
.get_nt_native_path ();
155 DWORD devn
= ((fhandler_base
*) dir
->__fh
)->get_device ();
156 /* Paths below /proc don't have a Win32 pendant. */
157 if (isproc_dev (devn
))
158 de
->d_ino
= hash_path_name (de
->d_ino
, L
"/");
159 else if (w32name
->Buffer
[w32name
->Length
/ sizeof (WCHAR
) - 1]
161 de
->d_ino
= hash_path_name (de
->d_ino
, L
"\\");
162 de
->d_ino
= hash_path_name (de
->d_ino
, de
->d_name
);
166 /* This fills out the old 32 bit d_ino field for old applications,
167 build under Cygwin before 1.5.x. */
168 de
->__d_internal1
= de
->d_ino
;
178 /* readdir: POSIX 5.1.2.1 */
179 extern "C" struct dirent
*
182 int res
= readdir_worker (dir
, dir
->__d_dirent
);
184 return dir
->__d_dirent
;
191 readdir_r (DIR *__restrict dir
, dirent
*__restrict de
, dirent
**__restrict ode
)
193 int res
= readdir_worker (dir
, de
);
206 ssize_t
posix_getdents(int fd
, void *buf
, size_t nbytes
, int flags
)
208 struct posix_dent
*dent_buf
, *src
;
211 cygheap_fdget
cfd (fd
);
212 /* Valid descriptor? */
215 /* Valid flags? Right now only DT_FORCE_TYPE is defined */
216 if ((flags
& ~DT_FORCE_TYPE
) != 0)
221 /* Create type-safe buffer pointer */
222 dent_buf
= (struct posix_dent
*) buf
;
223 /* Check if nbytes is big enough to fit at least one struct posix_dent */
224 if (nbytes
< sizeof (struct posix_dent
))
229 /* Create internal DIR * on first invocation */
230 if (!cfd
->getdents_dir ())
232 cfd
->getdents_dir (cfd
->opendir (fd
));
233 if (!cfd
->getdents_dir ())
236 /* Now loop until EOF or buf is full */
237 while (nbytes
>= sizeof (struct posix_dent
))
239 /* Our struct posix_dent is identical to struct dirent */
240 src
= (struct posix_dent
*) readdir (cfd
->getdents_dir ());
243 /* Handle the suggested DT_FORCE_TYPE flag */
244 if (src
->d_type
== DT_UNKNOWN
&& (flags
& DT_FORCE_TYPE
))
248 if (!fstatat (fd
, src
->d_name
, &st
, AT_SYMLINK_NOFOLLOW
))
249 src
->d_type
= IFTODT (st
.st_mode
);
253 nbytes
-= sizeof (struct posix_dent
);
255 return cnt
* sizeof (struct posix_dent
);
264 if (dir
->__d_cookie
== __DIRENT_COOKIE
)
265 return ((fhandler_base
*) dir
->__fh
)->telldir (dir
);
273 /* telldir was never defined using off_t in POSIX, only in early versions
274 of glibc. We have to keep the function in as entry point for backward
279 return (off_t
) telldir (dir
);
284 seekdir (DIR *dir
, long loc
)
288 if (dir
->__d_cookie
== __DIRENT_COOKIE
)
290 dir
->__flags
&= dirent_info_mask
;
291 ((fhandler_base
*) dir
->__fh
)->seekdir (dir
, loc
);
298 /* seekdir was never defined using off_t in POSIX, only in early versions
299 of glibc. We have to keep the function in as entry point for backward
302 seekdir64 (DIR *dir
, off_t loc
)
304 seekdir (dir
, (long) loc
);
307 /* rewinddir: POSIX 5.1.2.1 */
313 if (dir
->__d_cookie
== __DIRENT_COOKIE
)
315 dir
->__flags
&= dirent_info_mask
;
316 ((fhandler_base
*) dir
->__fh
)->rewinddir (dir
);
318 set_errno (EINVAL
); /* Diagnosis */
325 closedir_worker (DIR *dir
, int *fdret
)
329 if (dir
->__d_cookie
== __DIRENT_COOKIE
)
331 /* Reset the marker in case the caller tries to use `dir' again. */
334 int res
= ((fhandler_base
*) dir
->__fh
)->closedir (dir
);
337 *fdret
= dir
->__d_fd
;
341 free (dir
->__d_dirname
);
342 free (dir
->__d_dirent
);
353 /* closedir: POSIX 5.1.2.1 */
359 res
= closedir_worker (dir
, NULL
);
360 syscall_printf ("%R = closedir(%p)", res
, dir
);
365 fdclosedir (DIR *dir
)
369 closedir_worker (dir
, &fd
);
370 syscall_printf ("%d = fdclosedir(%p)", fd
, dir
);
374 /* mkdir: POSIX 5.4.1.1 */
376 mkdir (const char *dir
, mode_t mode
)
379 fhandler_base
*fh
= NULL
;
389 /* Following Linux, and intentionally ignoring POSIX, do not
390 resolve the last component of DIR if it is a symlink, even if
391 DIR has a trailing slash. Achieve this by stripping trailing
392 slashes or backslashes.
394 Exception: If DIR == 'x:' followed by one or more slashes or
395 backslashes, and if there's at least one backslash, assume
396 that the user is referring to the root directory of drive x.
397 Retain one backslash in this case. */
398 if (isdirsep (dir
[strlen (dir
) - 1]))
400 /* This converts // to /, but since both give EEXIST, we're okay. */
402 char *p
= stpcpy (buf
= tp
.c_get (), dir
) - 1;
405 while (p
> dir
&& isdirsep (*p
))
411 if (msdos
&& p
== dir
+ 1 && isdrive (dir
))
414 if (!(fh
= build_fh_name (dir
, PC_SYM_NOFOLLOW
)))
415 __leave
; /* errno already set */;
419 debug_printf ("got %d error from build_fh_name", fh
->error ());
420 set_errno (fh
->error ());
422 else if (fh
->exists ())
424 else if (has_dot_last_component (dir
, true))
426 else if (!fh
->mkdir (mode
))
432 syscall_printf ("%R = mkdir(%s, %d)", res
, dir
, mode
);
436 /* rmdir: POSIX 5.5.2.1 */
438 rmdir (const char *dir
)
441 fhandler_base
*fh
= NULL
;
451 /* Following Linux, and intentionally ignoring POSIX, do not
452 resolve the last component of DIR if it is a symlink, even if
453 DIR has a trailing slash. Achieve this by stripping trailing
454 slashes or backslashes.
456 Exception: If DIR == 'x:' followed by one or more slashes or
457 backslashes, and if there's at least one backslash, assume
458 that the user is referring to the root directory of drive x.
459 Retain one backslash in this case. */
460 if (isdirsep (dir
[strlen (dir
) - 1]))
462 /* This converts // to /, but since both give ENOTEMPTY,
465 char *p
= stpcpy (buf
= tp
.c_get (), dir
) - 1;
468 while (p
> dir
&& isdirsep (*p
))
474 if (msdos
&& p
== dir
+ 1 && isdrive (dir
))
477 if (!(fh
= build_fh_name (dir
, PC_SYM_NOFOLLOW
)))
478 __leave
; /* errno already set */;
482 debug_printf ("got %d error from build_fh_name", fh
->error ());
483 set_errno (fh
->error ());
485 else if (!fh
->exists ())
487 else if (has_dot_last_component (dir
, false))
489 else if (!fh
->rmdir ())
495 syscall_printf ("%R = rmdir(%s)", res
, dir
);