Whamcloud - gitweb
LU-586 1.8 <-> 2.1 interop: list_add corruption
[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-2006 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 #ifdef 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                 return -ENOTDIR;
105         if ((err = _sysio_permitted(pno, X_OK)) != 0)
106                 return err;
107
108         /*
109          * Release old if set.
110          */
111         if (_sysio_cwd)
112                 P_RELE(_sysio_cwd);
113
114         /*
115          * Finally, change to the new.
116          */
117         _sysio_cwd = pno;
118
119         return 0;
120 }
121
122 int
123 SYSIO_INTERFACE_NAME(chdir)(const char *path)
124 {
125         int     err;
126         struct pnode *pno;
127         SYSIO_INTERFACE_DISPLAY_BLOCK;
128
129         SYSIO_INTERFACE_ENTER;
130         err = _sysio_namei(_sysio_cwd, path, 0, NULL, &pno);
131         if (err)
132                 SYSIO_INTERFACE_RETURN(-1, err);
133
134         err = _sysio_p_chdir(pno);
135         if (err)
136                 P_RELE(pno);
137         SYSIO_INTERFACE_RETURN(err ? -1 : 0, err);
138 }
139
140 #ifdef REDSTORM
141 #undef __chdir
142 sysio_sym_weak_alias(SYSIO_INTERFACE_NAME(chdir),
143                      PREPEND(__, SYSIO_INTERFACE_NAME(chdir)))
144 #endif
145
146 /*
147  * Return path tracked by the path ancestor chain.
148  *
149  * If the buf pointer is NULL, a buffer large enough to hold the path
150  * is allocated from the heap.
151  */
152
153 static int
154 _sysio_p_path(struct pnode *pno, char **buf, size_t size)
155 {
156         struct pnode *cur;
157         size_t  len;
158         size_t  n;
159         char    *cp;
160
161         cur = pno;
162
163         if (!size && buf && *buf)
164                 return -EINVAL;
165
166         /*
167          * Walk up the tree to the root, summing the component name
168          * lengths and counting the vertices.
169          */
170         len = 0;
171         n = 0;
172         do {
173                 /*
174                  * If this is a covering path-node then the name should be
175                  * the *covered* nodes name, not this one unless we are at
176                  * the root of the name-space.
177                  */
178                 while (pno == pno->p_mount->mnt_root && pno != pno->p_parent )
179                         pno = pno->p_mount->mnt_covers;
180
181                 /*
182                  * Add length of this component to running sum and
183                  * account for this vertex.
184                  */
185                 assert((len >= pno->p_base->pb_name.len &&
186                         (size_t )~0 - pno->p_base->pb_name.len > len) ||
187                        (size_t )~0 - len > pno->p_base->pb_name.len);
188                 len += pno->p_base->pb_name.len;
189                 n++;
190                 assert(n);
191                 pno = pno->p_parent;
192         } while (pno != pno->p_parent);
193
194         if (!*buf)
195                 size = len + n + 1;
196         if (len >= size || n >= size - len)
197                 return -ERANGE;
198         if (!*buf) {
199                 /*
200                  * Allocate path buffer from the heap.
201                  */
202                 *buf = malloc(size * sizeof(char));
203                 if (!*buf)
204                         return -ENOMEM;
205         }
206
207         /*
208          * Fill in the path buffer.
209          */
210         pno = cur;
211         cp = *buf + len + n;
212         *cp = '\0';                                     /* NUL terminate */
213         do {
214                 /*
215                  * If this is a covering path-node then the name should be
216                  * the *covered* nodes name, not this one unless we are at
217                  * the root of the name-space.
218                  */
219                 while (pno == pno->p_mount->mnt_root && pno != pno->p_parent )
220                         pno = pno->p_mount->mnt_covers;
221
222                 /*
223                  * Add component and separator.
224                  */
225                 cp -= pno->p_base->pb_name.len;
226                 (void )memcpy(cp, pno->p_base->pb_name.name,
227                               pno->p_base->pb_name.len);
228
229                 *--cp = PATH_SEPARATOR;
230                 pno = pno->p_parent;
231         } while (pno != pno->p_parent);
232
233         return 0;
234 }
235
236 char *
237 SYSIO_INTERFACE_NAME(getcwd)(char *buf, size_t size)
238 {
239         int     err;
240         SYSIO_INTERFACE_DISPLAY_BLOCK;
241
242         SYSIO_INTERFACE_ENTER;
243 #ifdef DEFER_INIT_CWD
244         if (!_sysio_cwd) {
245                 struct pnode *pno;
246
247                 /*
248                  * Can no longer defer initialization of the current working
249                  * directory. Force namei to make it happen now.
250                  */
251                 if (_sysio_namei(NULL, ".", 0, NULL, &pno) != 0)
252                         abort();
253                 P_RELE(pno);
254         }
255 #endif
256         err = _sysio_p_path(_sysio_cwd, &buf, buf ? size : 0);
257         SYSIO_INTERFACE_RETURN(err ? NULL : buf, err);
258 }
259
260 #ifdef __GLIBC__
261 #undef __getcwd
262 sysio_sym_weak_alias(SYSIO_INTERFACE_NAME(getcwd), 
263                      PREPEND(__, SYSIO_INTERFACE_NAME(getcwd)))
264 #endif
265
266 #if defined(PATH_MAX) && !(defined(REDSTORM))
267 char    *
268 SYSIO_INTERFACE_NAME(getwd)(char *buf)
269 {
270
271         if (!buf) {
272                 errno = EFAULT;
273                 return NULL;
274         }
275
276         return SYSIO_INTERFACE_NAME(getcwd)(buf, PATH_MAX);
277 }
278 #endif