Whamcloud - gitweb
b=10466
[fs/lustre-release.git] / libsysio / src / chdir.c
1 /*
2  *    This Cplant(TM) source code is the property of Sandia National
3  *    Laboratories.
4  *
5  *    This Cplant(TM) source code is copyrighted by Sandia National
6  *    Laboratories.
7  *
8  *    The redistribution of this Cplant(TM) source code is subject to the
9  *    terms of the GNU Lesser General Public License
10  *    (see cit/LGPL or http://www.gnu.org/licenses/lgpl.html)
11  *
12  *    Cplant(TM) Copyright 1998-2003 Sandia Corporation. 
13  *    Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
14  *    license for use of this work by or on behalf of the US Government.
15  *    Export of this program may require a license from the United States
16  *    Government.
17  */
18
19 /*
20  * #############################################################################
21  * #
22  * #     This Cplant(TM) source code is the property of Sandia National
23  * #     Laboratories.
24  * #
25  * #     This Cplant(TM) source code is copyrighted by Sandia National
26  * #     Laboratories.
27  * #
28  * #     The redistribution of this Cplant(TM) source code is subject to the
29  * #     terms of the GNU Lesser General Public License
30  * #     (see cit/LGPL or http://www.gnu.org/licenses/lgpl.html)
31  * #
32  * #     Cplant(TM) Copyright 1998-2003 Sandia Corporation. 
33  * #     Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
34  * #     license for use of this work by or on behalf of the US Government.
35  * #     Export of this program may require a license from the United States
36  * #     Government.
37  * #
38  * #############################################################################
39  */
40
41 /*
42  * This library is free software; you can redistribute it and/or
43  * modify it under the terms of the GNU Lesser General Public
44  * License as published by the Free Software Foundation; either
45  * version 2.1 of the License, or (at your option) any later version.
46  * 
47  * This library is distributed in the hope that it will be useful,
48  * but WITHOUT ANY WARRANTY; without even the implied warranty of
49  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
50  * Lesser General Public License for more details.
51  * 
52  * You should have received a copy of the GNU Lesser General Public
53  * License along with this library; if not, write to the Free Software
54  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
55  *
56  * Questions or comments about this library should be sent to:
57  *
58  * Lee Ward
59  * Sandia National Laboratories, New Mexico
60  * P.O. Box 5800
61  * Albuquerque, NM 87185-1110
62  *
63  * lee@sandia.gov
64  */
65
66 #include <stdlib.h>
67 #include <sys/types.h>
68 #include <sys/stat.h>
69 #include <unistd.h>
70 #include <string.h>
71 #include <limits.h>
72 #include <errno.h>
73 #include <assert.h>
74 #include <sys/queue.h>
75
76 #include "sysio.h"
77 #include "inode.h"
78 #include "mount.h"
79 #include "file.h"
80 #include "sysio-symbols.h"
81
82 #if DEFER_INIT_CWD
83 const char *_sysio_init_cwd = NULL;
84 #endif
85
86 struct pnode *_sysio_cwd = NULL;
87
88 /*
89  * Change to directory specified by the given pnode.
90  */
91 int
92 _sysio_p_chdir(struct pnode *pno)
93 {
94         int     err;
95
96         /*
97          * Revalidate the pnode, and ensure it's an accessable directory
98          */
99         err = _sysio_p_validate(pno, NULL, NULL);
100         if (err)
101                 return err;
102         if (!(pno->p_base->pb_ino &&
103               S_ISDIR(pno->p_base->pb_ino->i_stbuf.st_mode)))
104                 err = -ENOTDIR;
105         else
106                 err = _sysio_permitted(pno->p_base->pb_ino, X_OK);
107         if (err)
108                 return err;
109
110         /*
111          * Release old if set.
112          */
113         if (_sysio_cwd)
114                 P_RELE(_sysio_cwd);
115
116         /*
117          * Finally, change to the new.
118          */
119         _sysio_cwd = pno;
120
121         return 0;
122 }
123
124 int
125 SYSIO_INTERFACE_NAME(chdir)(const char *path)
126 {
127         int     err;
128         struct pnode *pno;
129         SYSIO_INTERFACE_DISPLAY_BLOCK;
130
131         SYSIO_INTERFACE_ENTER;
132         err = _sysio_namei(_sysio_cwd, path, 0, NULL, &pno);
133         if (err)
134                 SYSIO_INTERFACE_RETURN(-1, err);
135
136         err = _sysio_p_chdir(pno);
137         if (err)
138                 P_RELE(pno);
139         SYSIO_INTERFACE_RETURN(err ? -1 : 0, err);
140 }
141
142 #ifdef REDSTORM
143 #undef __chdir
144 sysio_sym_weak_alias(SYSIO_INTERFACE_NAME(chdir),
145                      PREPEND(__, SYSIO_INTERFACE_NAME(chdir)))
146 #endif
147
148 /*
149  * Return path tracked by the path ancestor chain.
150  *
151  * If the buf pointer is NULL, a buffer large enough to hold the path
152  * is allocated from the heap.
153  */
154
155 static int
156 _sysio_p_path(struct pnode *pno, char **buf, size_t size)
157 {
158         struct pnode *cur;
159         size_t  len;
160         size_t  n;
161         char    *cp;
162
163         cur = pno;
164
165         if (!size && buf && *buf)
166                 return -EINVAL;
167
168         /*
169          * Walk up the tree to the root, summing the component name
170          * lengths and counting the vertices.
171          */
172         len = 0;
173         n = 0;
174         do {
175                 /*
176                  * If this is a covering path-node then the name should be
177                  * the *covered* nodes name, not this one unless we are at
178                  * the root of the name-space.
179                  */
180                 while (pno == pno->p_mount->mnt_root && pno != pno->p_parent )
181                         pno = pno->p_mount->mnt_covers;
182
183                 /*
184                  * Add length of this component to running sum and
185                  * account for this vertex.
186                  */
187                 assert((len >= pno->p_base->pb_name.len &&
188                         (size_t )~0 - pno->p_base->pb_name.len > len) ||
189                        (size_t )~0 - len > pno->p_base->pb_name.len);
190                 len += pno->p_base->pb_name.len;
191                 n++;
192                 assert(n);
193                 pno = pno->p_parent;
194         } while (pno != pno->p_parent);
195
196         if (!*buf)
197                 size = len + n + 1;
198         if (len >= size || n >= size - len)
199                 return -ERANGE;
200         if (!*buf) {
201                 /*
202                  * Allocate path buffer from the heap.
203                  */
204                 *buf = malloc(size * sizeof(char));
205                 if (!*buf)
206                         return -ENOMEM;
207         }
208
209         /*
210          * Fill in the path buffer.
211          */
212         pno = cur;
213         cp = *buf + len + n;
214         *cp = '\0';                                     /* NUL terminate */
215         do {
216                 /*
217                  * If this is a covering path-node then the name should be
218                  * the *covered* nodes name, not this one unless we are at
219                  * the root of the name-space.
220                  */
221                 while (pno == pno->p_mount->mnt_root && pno != pno->p_parent )
222                         pno = pno->p_mount->mnt_covers;
223
224                 /*
225                  * Add component and separator.
226                  */
227                 cp -= pno->p_base->pb_name.len;
228                 (void )memcpy(cp, pno->p_base->pb_name.name,
229                               pno->p_base->pb_name.len);
230
231                 *--cp = PATH_SEPARATOR;
232                 pno = pno->p_parent;
233         } while (pno != pno->p_parent);
234
235         return 0;
236 }
237
238 char *
239 SYSIO_INTERFACE_NAME(getcwd)(char *buf, size_t size)
240 {
241         int     err;
242         SYSIO_INTERFACE_DISPLAY_BLOCK;
243
244         SYSIO_INTERFACE_ENTER;
245 #if DEFER_INIT_CWD
246         if (!_sysio_cwd) {
247                 struct pnode *pno;
248
249                 /*
250                  * Can no longer defer initialization of the current working
251                  * directory. Force namei to make it happen now.
252                  */
253                 if (_sysio_namei(NULL, ".", 0, NULL, &pno) != 0)
254                         abort();
255                 P_RELE(pno);
256         }
257 #endif
258         err = _sysio_p_path(_sysio_cwd, &buf, buf ? size : 0);
259         SYSIO_INTERFACE_RETURN(err ? NULL : buf, err);
260 }
261
262 #ifdef __GLIBC__
263 #undef __getcwd
264 sysio_sym_weak_alias(SYSIO_INTERFACE_NAME(getcwd), 
265                      PREPEND(__, SYSIO_INTERFACE_NAME(getcwd)))
266 #endif
267
268 #if defined(PATH_MAX) && !(defined(REDSTORM))
269 char    *
270 SYSIO_INTERFACE_NAME(getwd)(char *buf)
271 {
272
273         if (!buf) {
274                 errno = EFAULT;
275                 return NULL;
276         }
277
278         return SYSIO_INTERFACE_NAME(getcwd)(buf, PATH_MAX);
279 }
280 #endif