1 : /* provide a replacement fdopendir function
2 : Copyright (C) 2004-2009 Free Software Foundation, Inc.
3 :
4 : This program is free software: you can redistribute it and/or modify
5 : it under the terms of the GNU General Public License as published by
6 : the Free Software Foundation; either version 3 of the License, or
7 : (at your option) any later version.
8 :
9 : This program is distributed in the hope that it will be useful,
10 : but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : GNU General Public License for more details.
13 :
14 : You should have received a copy of the GNU General Public License
15 : along with this program. If not, see <http://www.gnu.org/licenses/>. */
16 :
17 : /* written by Jim Meyering */
18 :
19 : #include <config.h>
20 :
21 : #include <dirent.h>
22 :
23 : #include <stdlib.h>
24 : #include <unistd.h>
25 :
26 : #if !HAVE_FDOPENDIR
27 :
28 : # include "openat.h"
29 : # include "openat-priv.h"
30 : # include "save-cwd.h"
31 :
32 : # if GNULIB_DIRENT_SAFER
33 : # include "dirent--.h"
34 : # endif
35 :
36 : /* Replacement for Solaris' function by the same name.
37 : <http://www.google.com/search?q=fdopendir+site:docs.sun.com>
38 : First, try to simulate it via opendir ("/proc/self/fd/FD"). Failing
39 : that, simulate it by using fchdir metadata, or by doing
40 : save_cwd/fchdir/opendir(".")/restore_cwd.
41 : If either the save_cwd or the restore_cwd fails (relatively unlikely),
42 : then give a diagnostic and exit nonzero.
43 : Otherwise, this function works just like Solaris' fdopendir.
44 :
45 : W A R N I N G:
46 : Unlike other fd-related functions, this one effectively consumes
47 : its FD parameter. The caller should not close or otherwise
48 : manipulate FD if this function returns successfully. Also, this
49 : implementation does not guarantee that dirfd(fdopendir(n))==n;
50 : the open directory stream may use a clone of FD, or have no
51 : associated fd at all. */
52 : DIR *
53 : fdopendir (int fd)
54 : {
55 : int saved_errno;
56 : DIR *dir;
57 :
58 : char buf[OPENAT_BUFFER_SIZE];
59 : char *proc_file = openat_proc_name (buf, fd, ".");
60 : if (proc_file)
61 : {
62 : dir = opendir (proc_file);
63 : saved_errno = errno;
64 : }
65 : else
66 : {
67 : dir = NULL;
68 : saved_errno = EOPNOTSUPP;
69 : }
70 :
71 : /* If the syscall fails with an expected errno value, resort to
72 : save_cwd/restore_cwd. */
73 : if (! dir && EXPECTED_ERRNO (saved_errno))
74 : {
75 : # if REPLACE_FCHDIR
76 : const char *name = _gl_directory_name (fd);
77 : if (name)
78 : dir = opendir (name);
79 : saved_errno = errno;
80 : # else /* !REPLACE_FCHDIR */
81 : struct saved_cwd saved_cwd;
82 : if (save_cwd (&saved_cwd) != 0)
83 : openat_save_fail (errno);
84 :
85 : if (fchdir (fd) != 0)
86 : {
87 : dir = NULL;
88 : saved_errno = errno;
89 : }
90 : else
91 : {
92 : dir = opendir (".");
93 : saved_errno = errno;
94 :
95 : if (restore_cwd (&saved_cwd) != 0)
96 : openat_restore_fail (errno);
97 : }
98 :
99 : free_cwd (&saved_cwd);
100 : # endif /* !REPLACE_FCHDIR */
101 : }
102 :
103 : if (dir)
104 : close (fd);
105 : if (proc_file != buf)
106 : free (proc_file);
107 : errno = saved_errno;
108 : return dir;
109 : }
110 :
111 : #else /* HAVE_FDOPENDIR */
112 :
113 : # include <errno.h>
114 : # include <sys/stat.h>
115 :
116 : # undef fdopendir
117 :
118 : /* Like fdopendir, but work around GNU/Hurd bug by validating FD. */
119 :
120 : DIR *
121 : rpl_fdopendir (int fd)
122 42 : {
123 : struct stat st;
124 42 : if (fstat (fd, &st))
125 1 : return NULL;
126 41 : if (!S_ISDIR (st.st_mode))
127 : {
128 1 : errno = ENOTDIR;
129 1 : return NULL;
130 : }
131 40 : return fdopendir (fd);
132 : }
133 :
134 : #endif /* HAVE_FDOPENDIR */
|