Cygwin: strptime: add release note
[newlib-cygwin.git] / winsup / cygwin / dir.cc
blobbe999414a7abb37b4a9dbfb1ab91358a63daf33d
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
7 details. */
9 #include "winsup.h"
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <sys/stat.h>
14 #define _LIBC
15 #include <dirent.h>
17 #include "cygerrno.h"
18 #include "security.h"
19 #include "path.h"
20 #include "fhandler.h"
21 #include "dtable.h"
22 #include "cygheap.h"
23 #include "cygtls.h"
24 #include "tls_pbuf.h"
26 extern "C" int
27 dirfd (DIR *dir)
29 __try
31 if (dir->__d_cookie == __DIRENT_COOKIE)
32 return dir->__d_fd;
33 syscall_printf ("-1 = dirfd (%p)", dir);
34 set_errno (EINVAL);
36 __except (EFAULT) {}
37 __endtry
38 return -1;
41 /* Symbol kept for backward compatibility. Don't remove. Don't declare
42 in public header file. */
43 extern "C" DIR *
44 __opendir_with_d_ino (const char *name)
46 return opendir (name);
49 /* opendir: POSIX 5.1.2.1 */
50 extern "C" DIR *
51 opendir (const char *name)
53 fhandler_base *fh;
54 DIR *res;
56 fh = build_fh_name (name, PC_SYM_FOLLOW);
57 if (!fh)
58 res = NULL;
59 else if (fh->error ())
61 set_errno (fh->error ());
62 res = NULL;
64 else if (fh->exists ())
65 res = fh->opendir (-1);
66 else
68 set_errno (ENOENT);
69 res = NULL;
72 if (!res && fh)
73 delete fh;
74 /* Applications calling flock(2) on dirfd(fd) need this... */
75 if (res && !fh->nohandle ())
76 fh->set_unique_id ();
77 return res;
80 extern "C" DIR *
81 fdopendir (int fd)
83 DIR *res = NULL;
85 cygheap_fdget cfd (fd);
86 if (cfd >= 0)
87 res = cfd->opendir (fd);
88 return res;
91 static int
92 readdir_worker (DIR *dir, dirent *de)
94 int res = 0;
96 __try
98 if (dir->__d_cookie != __DIRENT_COOKIE)
100 syscall_printf ("%p = readdir (%p)", NULL, dir);
101 res = EBADF;
102 __leave;
105 de->d_ino = 0;
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);
112 if (res == ENMFILE)
114 if (!(dir->__flags & dirent_saw_dot))
116 strcpy (de->d_name, ".");
117 dir->__flags |= dirent_saw_dot;
118 dir->__d_position++;
119 res = 0;
121 else if (!(dir->__flags & dirent_saw_dot_dot))
123 strcpy (de->d_name, "..");
124 dir->__flags |= dirent_saw_dot_dot;
125 dir->__d_position++;
126 res = 0;
130 if (!res && !de->d_ino)
132 bool is_dot = false;
133 bool is_dot_dot = false;
135 if (de->d_name[0] == '.')
137 if (de->d_name[1] == '\0')
138 is_dot = true;
139 else if (de->d_name[1] == '.' && de->d_name[2] == '\0')
140 is_dot_dot = true;
143 if (is_dot_dot && !(dir->__flags & dirent_isroot))
144 de->d_ino = readdir_get_ino (((fhandler_base *)
145 dir->__fh)->get_name (),
146 true);
147 else
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]
160 != L'\\')
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;
170 __except (NO_ERROR)
172 res = EFAULT;
174 __endtry
175 return res;
178 /* readdir: POSIX 5.1.2.1 */
179 extern "C" struct dirent *
180 readdir (DIR *dir)
182 int res = readdir_worker (dir, dir->__d_dirent);
183 if (res == 0)
184 return dir->__d_dirent;
185 if (res != ENMFILE)
186 set_errno (res);
187 return NULL;
190 extern "C" int
191 readdir_r (DIR *__restrict dir, dirent *__restrict de, dirent **__restrict ode)
193 int res = readdir_worker (dir, de);
194 if (!res)
195 *ode = de;
196 else
198 *ode = NULL;
199 if (res == ENMFILE)
200 res = 0;
202 return res;
205 extern "C"
206 ssize_t posix_getdents(int fd, void *buf, size_t nbytes, int flags)
208 struct posix_dent *dent_buf, *src;
209 ssize_t cnt = 0;
211 cygheap_fdget cfd (fd);
212 /* Valid descriptor? */
213 if (cfd < 0)
214 return -1;
215 /* Valid flags? Right now only DT_FORCE_TYPE is defined */
216 if ((flags & ~DT_FORCE_TYPE) != 0)
218 set_errno (EINVAL);
219 return -1;
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))
226 set_errno (EINVAL);
227 return -1;
229 /* Create internal DIR * on first invocation */
230 if (!cfd->getdents_dir ())
232 cfd->getdents_dir (cfd->opendir (fd));
233 if (!cfd->getdents_dir ())
234 return -1;
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 ());
241 if (!src)
242 break;
243 /* Handle the suggested DT_FORCE_TYPE flag */
244 if (src->d_type == DT_UNKNOWN && (flags & DT_FORCE_TYPE))
246 struct stat st;
248 if (!fstatat (fd, src->d_name, &st, AT_SYMLINK_NOFOLLOW))
249 src->d_type = IFTODT (st.st_mode);
251 *dent_buf++ = *src;
252 ++cnt;
253 nbytes -= sizeof (struct posix_dent);
255 return cnt * sizeof (struct posix_dent);
258 /* telldir */
259 extern "C" long
260 telldir (DIR *dir)
262 __try
264 if (dir->__d_cookie == __DIRENT_COOKIE)
265 return ((fhandler_base *) dir->__fh)->telldir (dir);
266 set_errno (EBADF);
268 __except (EFAULT) {}
269 __endtry
270 return -1;
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
275 compatibility. */
276 extern "C" off_t
277 telldir64 (DIR *dir)
279 return (off_t) telldir (dir);
282 /* seekdir */
283 extern "C" void
284 seekdir (DIR *dir, long loc)
286 __try
288 if (dir->__d_cookie == __DIRENT_COOKIE)
290 dir->__flags &= dirent_info_mask;
291 ((fhandler_base *) dir->__fh)->seekdir (dir, loc);
294 __except (EFAULT) {}
295 __endtry
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
300 compatibility. */
301 extern "C" void
302 seekdir64 (DIR *dir, off_t loc)
304 seekdir (dir, (long) loc);
307 /* rewinddir: POSIX 5.1.2.1 */
308 extern "C" void
309 rewinddir (DIR *dir)
311 __try
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 */
320 __except (EFAULT) {}
321 __endtry
324 static int
325 closedir_worker (DIR *dir, int *fdret)
327 __try
329 if (dir->__d_cookie == __DIRENT_COOKIE)
331 /* Reset the marker in case the caller tries to use `dir' again. */
332 dir->__d_cookie = 0;
334 int res = ((fhandler_base *) dir->__fh)->closedir (dir);
336 if (fdret)
337 *fdret = dir->__d_fd;
338 else
339 close (dir->__d_fd);
341 free (dir->__d_dirname);
342 free (dir->__d_dirent);
343 free (dir);
344 return res;
346 set_errno (EBADF);
348 __except (EFAULT) {}
349 __endtry
350 return -1;
353 /* closedir: POSIX 5.1.2.1 */
354 extern "C" int
355 closedir (DIR *dir)
357 int res;
359 res = closedir_worker (dir, NULL);
360 syscall_printf ("%R = closedir(%p)", res, dir);
361 return res;
364 extern "C" int
365 fdclosedir (DIR *dir)
367 int fd = -1;
369 closedir_worker (dir, &fd);
370 syscall_printf ("%d = fdclosedir(%p)", fd, dir);
371 return fd;
374 /* mkdir: POSIX 5.4.1.1 */
375 extern "C" int
376 mkdir (const char *dir, mode_t mode)
378 int res = -1;
379 fhandler_base *fh = NULL;
380 tmp_pathbuf tp;
382 __try
384 if (!*dir)
386 set_errno (ENOENT);
387 __leave;
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. */
401 char *buf;
402 char *p = stpcpy (buf = tp.c_get (), dir) - 1;
403 bool msdos = false;
404 dir = buf;
405 while (p > dir && isdirsep (*p))
407 if (*p == '\\')
408 msdos = true;
409 *p-- = '\0';
411 if (msdos && p == dir + 1 && isdrive (dir))
412 p[1] = '\\';
414 if (!(fh = build_fh_name (dir, PC_SYM_NOFOLLOW)))
415 __leave; /* errno already set */;
417 if (fh->error ())
419 debug_printf ("got %d error from build_fh_name", fh->error ());
420 set_errno (fh->error ());
422 else if (fh->exists ())
423 set_errno (EEXIST);
424 else if (has_dot_last_component (dir, true))
425 set_errno (ENOENT);
426 else if (!fh->mkdir (mode))
427 res = 0;
428 delete fh;
430 __except (EFAULT) {}
431 __endtry
432 syscall_printf ("%R = mkdir(%s, %d)", res, dir, mode);
433 return res;
436 /* rmdir: POSIX 5.5.2.1 */
437 extern "C" int
438 rmdir (const char *dir)
440 int res = -1;
441 fhandler_base *fh = NULL;
442 tmp_pathbuf tp;
444 __try
446 if (!*dir)
448 set_errno (ENOENT);
449 __leave;
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,
463 we're okay. */
464 char *buf;
465 char *p = stpcpy (buf = tp.c_get (), dir) - 1;
466 bool msdos = false;
467 dir = buf;
468 while (p > dir && isdirsep (*p))
470 if (*p == '\\')
471 msdos = true;
472 *p-- = '\0';
474 if (msdos && p == dir + 1 && isdrive (dir))
475 p[1] = '\\';
477 if (!(fh = build_fh_name (dir, PC_SYM_NOFOLLOW)))
478 __leave; /* errno already set */;
480 if (fh->error ())
482 debug_printf ("got %d error from build_fh_name", fh->error ());
483 set_errno (fh->error ());
485 else if (!fh->exists ())
486 set_errno (ENOENT);
487 else if (has_dot_last_component (dir, false))
488 set_errno (EINVAL);
489 else if (!fh->rmdir ())
490 res = 0;
491 delete fh;
493 __except (EFAULT) {}
494 __endtry
495 syscall_printf ("%R = rmdir(%s)", res, dir);
496 return res;