Whamcloud - gitweb
b=10466
[fs/lustre-release.git] / libsysio / src / namei.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  * This library is free software; you can redistribute it and/or
21  * modify it under the terms of the GNU Lesser General Public
22  * License as published by the Free Software Foundation; either
23  * version 2.1 of the License, or (at your option) any later version.
24  * 
25  * This library is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28  * Lesser General Public License for more details.
29  * 
30  * You should have received a copy of the GNU Lesser General Public
31  * License along with this library; if not, write to the Free Software
32  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33  *
34  * Questions or comments about this library should be sent to:
35  *
36  * Lee Ward
37  * Sandia National Laboratories, New Mexico
38  * P.O. Box 5800
39  * Albuquerque, NM 87185-1110
40  *
41  * lee@sandia.gov
42  */
43
44 #if defined(AUTOMOUNT_FILE_NAME) && defined(__linux__)
45 #define _BSD_SOURCE
46 #endif
47
48 #include <stdlib.h>
49 #include <string.h>
50 #include <errno.h>
51 #include <assert.h>
52 #include <unistd.h>
53 #include <sys/param.h>
54 #include <sys/types.h>
55 #include <sys/stat.h>
56 #include <sys/queue.h>
57
58 #include "sysio.h"
59 #include "mount.h"
60 #include "inode.h"
61
62 /*
63  * Parse next component in path.
64  */
65 #ifndef AUTOMOUNT_FILE_NAME
66 static
67 #endif
68 void
69 _sysio_next_component(const char *path, struct qstr *name)
70 {
71         while (*path == PATH_SEPARATOR)
72                 path++;
73         name->name = path;
74         name->len = 0;
75         name->hashval = 0;
76         while (*path && *path != PATH_SEPARATOR) {
77                 name->hashval =
78                     37 * name->hashval + *path++;
79                 name->len++;
80         }
81 }
82
83 /*
84  * Given parent, look up component.
85  */
86 static int
87 lookup(struct pnode *parent,
88        struct qstr *name,
89        struct pnode **pnop,
90        struct intent *intnt,
91        const char *path)
92 {
93         int     err;
94         struct pnode *pno;
95
96         if (!parent->p_base->pb_ino)
97                 return -ENOTDIR;
98
99         err = _sysio_permitted(parent->p_base->pb_ino, X_OK);
100         if (err)
101                 return err;
102
103         /*
104          * Short-circuit `.' and `..'; We don't cache those.
105          */
106         pno = NULL;
107         if (name->len == 1 && name->name[0] == '.')
108                 pno = parent;
109         else if (name->len == 2 && name->name[0] == '.' && name->name[1] == '.')
110                 pno = parent->p_parent;
111         if (pno)
112                 P_REF(pno);
113         else {
114                 /*
115                  * Get cache entry then.
116                  */
117                 err = _sysio_p_find_alias(parent, name, &pno);
118                 if (err)
119                         return err;
120         }
121
122         /*
123          * While covered, move to the covering node.
124          */
125         while (pno->p_cover && pno->p_cover != pno) {
126                 struct pnode *cover;
127
128                 cover = pno->p_cover;
129                 P_REF(cover);
130                 P_RELE(pno);
131                 pno = cover;
132         }
133
134         *pnop = pno;
135
136         /*
137          * (Re)validate the pnode.
138          */
139         err = _sysio_p_validate(pno, intnt, path);
140         if (err)
141                 return err;
142
143         return 0;
144 }
145
146 /*
147  * The meat. Walk an absolute or relative path, looking up each
148  * component. Various flags in the nameidata argument govern actions
149  * and return values/state. They are:
150  *
151  * ND_NOFOLLOW          symbolic links are not followed
152  * ND_NEGOK             if terminal/leaf does not exist, return
153  *                       path node (alias) anyway.
154  */
155 int
156 _sysio_path_walk(struct pnode *parent, struct nameidata *nd)
157 {
158         int     err;
159         const char *path;
160         struct qstr this, next;
161         struct inode *ino;
162
163         /*
164          * NULL path?
165          */
166         if (!nd->nd_path)
167                 return -EFAULT;
168
169         /*
170          * Empty path?
171          */
172         if (!*nd->nd_path)
173                 return -ENOENT;
174
175         /*
176          * Leading slash?
177          */
178         if (*nd->nd_path == PATH_SEPARATOR) {
179                 /*
180                  * Make parent the root of the name space.
181                  */
182                 parent = nd->nd_root;
183         }
184
185 #if DEFER_INIT_CWD
186         if (!parent) {
187                 const char *icwd;
188
189                 if (!_sysio_init_cwd && !nd->nd_root)
190                         abort();
191
192                 /*
193                  * Finally have to set the current working directory. We can
194                  * not tolerate errors here or else risk leaving the process
195                  * in a very unexpected location. We abort then unless all goes
196                  * well.
197                  */
198                 icwd = _sysio_init_cwd;
199                 _sysio_init_cwd = NULL;
200                 parent = nd->nd_root;
201                 if (!parent)
202                         abort();
203                 (void )_sysio_namei(nd->nd_root, icwd, 0, NULL, &parent);
204                 if (_sysio_p_chdir(parent) != 0)
205                         abort();
206         }
207 #endif
208
209         /*
210          * (Re)Validate the parent.
211          */
212         err = _sysio_p_validate(parent, NULL, NULL);
213         if (err)
214                 return err;
215
216         /*
217          * Prime everything for the loop. Will need another reference to the
218          * initial directory. It'll be dropped later.
219          */
220         nd->nd_pno = parent;
221         P_REF(nd->nd_pno);
222         _sysio_next_component(nd->nd_path, &next);
223         path = next.name;
224         parent = NULL;
225         err = 0;
226
227         /*
228          * Derecurse the path tree-walk.
229          */
230         for (;;) {
231                 ino = nd->nd_pno->p_base->pb_ino;
232                 if (S_ISLNK(ino->i_stbuf.st_mode) &&
233                     (next.len || !(nd->nd_flags & ND_NOFOLLOW))) {
234                         char    *lpath;
235                         ssize_t cc;
236                         struct nameidata nameidata;
237
238                         if (nd->nd_slicnt >= MAX_SYMLINK) {
239                                 err = -ELOOP;
240                                 break;
241                         }
242
243                         /*
244                          * Follow symbolic link.
245                          */
246                         lpath = malloc(MAXPATHLEN + 1);
247                         if (!lpath) {
248                                 err = -ENOMEM;
249                                 break;
250                         }
251                         cc =
252                             ino->i_ops.inop_readlink(nd->nd_pno,
253                                                      lpath,
254                                                      MAXPATHLEN);
255                         if (cc < 0) {
256                                 free(lpath);
257                                 err = (int )cc;
258                                 break;
259                         }
260                         lpath[cc] = '\0';                       /* NUL term */
261                         /*
262                          * Handle symbolic links with recursion. Yuck!
263                          * Pass the NULL intent for recursive symlink
264                          * except the last component.
265                          */
266                         ND_INIT(&nameidata,
267                                 (nd->nd_flags | ND_NEGOK),
268                                 lpath,
269                                 nd->nd_root,
270                                 !next.len ? nd->nd_intent : NULL);
271                         nameidata.nd_slicnt = nd->nd_slicnt + 1;
272                         err =
273                             _sysio_path_walk(nd->nd_pno->p_parent, &nameidata);
274                         free(lpath);
275                         if (err)
276                                 break;
277                         P_RELE(nd->nd_pno);
278                         nd->nd_pno = nameidata.nd_pno;
279                         ino = nd->nd_pno->p_base->pb_ino;
280                 }
281 #ifdef AUTOMOUNT_FILE_NAME
282                 else if (ino &&
283                          S_ISDIR(ino->i_stbuf.st_mode) &&
284                          (nd->nd_pno->p_mount->mnt_flags & MOUNT_F_AUTO) &&
285                          nd->nd_amcnt < MAX_MOUNT_DEPTH &&
286                          ino->i_stbuf.st_mode & S_ISUID) {
287                         struct pnode *pno;
288
289                         /*
290                          * We're committed to a lookup. It's time to see if
291                          * we're going to do it in an automount-point and
292                          * arrange the mount if so.
293                          */
294                         assert(!nd->nd_pno->p_cover);
295                         err =
296                             lookup(nd->nd_pno,
297                                    &_sysio_mount_file_name,
298                                    &pno,
299                                    NULL,
300                                    NULL);
301                         if (pno)
302                                 P_RELE(pno);
303                         if (!err && _sysio_automount(pno) == 0) {
304                                 struct pnode *root;
305
306                                 /*
307                                  * All went well. Need to switch
308                                  * parent pno and ino to the
309                                  * root of the newly mounted sub-tree.
310                                  *
311                                  * NB:
312                                  * We don't recurseively retry these
313                                  * things. It's OK to have the new root
314                                  * be an automount-point but it's going
315                                  * to take another lookup to accomplish it.
316                                  * The alternative could get us into an
317                                  * infinite loop.
318                                  */
319                                 root = nd->nd_pno->p_cover;
320                                 assert(root);
321                                 P_RELE(nd->nd_pno);
322                                 nd->nd_pno = root;
323 #if 0
324                                 P_REF(nd->nd_pno);
325 #endif
326                                 ino = nd->nd_pno->p_base->pb_ino;
327                                 assert(ino);
328
329                                 /*
330                                  * Must send the intent-path again.
331                                  */
332                                 path = nd->nd_path;
333                                 nd->nd_amcnt++;
334
335                                 /*
336                                  * Must go back top and retry with this
337                                  * new pnode as parent.
338                                  */
339                                 continue;
340                         }
341                         err = 0;                        /* it never happened */
342                 }
343 #endif
344
345                 /*
346                  * Set up for next component.
347                  */
348                 this = next;
349                 if (path)
350                         path = this.name;
351                 if (!this.len)
352                         break;
353                 if (!ino) {
354                         /*
355                          * Should only be here if final component was
356                          * target of a symlink.
357                          */
358                         nd->nd_path = this.name + this.len;
359                         err = -ENOENT;
360                         break;
361                 }
362                 nd->nd_path = this.name + this.len;
363                 _sysio_next_component(nd->nd_path, &next);
364                 parent = nd->nd_pno;
365                 nd->nd_pno = NULL;
366
367                 /*
368                  * Parent must be a directory.
369                  */
370                 if (ino && !S_ISDIR(ino->i_stbuf.st_mode)) {
371                         err = -ENOTDIR;
372                         break;
373                 }
374
375                 /*
376                  * The extra path arg is passed only on the first lookup in the
377                  * walk as we cross into each file system, anew. The intent is
378                  * passed both on the first lookup and when trying to look up
379                  * the final component -- Of the original path, not on the
380                  * file system.
381                  *
382                  * Confused? Me too and I came up with this weirdness. It's
383                  * hints to the file system drivers. Read on.
384                  *
385                  * The first lookup will give everything one needs to ready
386                  * everything for the entire operation before the path is
387                  * walked. The file system driver knows it's the first lookup
388                  * in the walk because it has both the path and the intent.
389                  *
390                  * Alternatively, one could split the duties; The first lookup
391                  * can be used to prime the file system inode cache with the
392                  * interior nodes we'll want in the path-walk. Then, when
393                  * looking up the last component, ready everything for the
394                  * operations(s) to come. The file system driver knows it's
395                  * the last lookup in the walk because it has the intent,
396                  * again, but without the path.
397                  *
398                  * One special case; If we were asked to look up a single
399                  * component, we treat it as the last component. The file
400                  * system driver never sees the extra path argument. It should
401                  * be noted that the driver always has the fully qualified
402                  * path, on the target file system, available to it for any
403                  * node it is looking up, including the last, via the base
404                  * path node and it's ancestor chain.
405                  */
406                 err =
407                     lookup(parent,
408                            &this,
409                            &nd->nd_pno,
410                            (path || !next.len)
411                              ? nd->nd_intent
412                              : NULL,
413                            (path && next.len) ? path : NULL);
414                 if (err) {
415                         if (err == -ENOENT &&
416                             !next.len &&
417                             (nd->nd_flags & ND_NEGOK))
418                                 err = 0;
419                         break;
420                 }
421                 path = NULL;                            /* Stop that! */
422                 if ((parent->p_mount->mnt_fs !=
423                      nd->nd_pno->p_mount->mnt_fs)) {
424                         /*
425                          * Crossed into a new fs. We'll want the next lookup
426                          * to include the path again.
427                          */
428                         path = nd->nd_path;
429                 }
430
431                 /*
432                  * Release the parent.
433                  */
434                 P_RELE(parent);
435                 parent = NULL;
436         }
437
438         /*
439          * Trailing separators cause us to break from the loop with
440          * a parent set but no pnode. Check for that.
441          */
442         if (!nd->nd_pno) {
443                 nd->nd_pno = parent;
444                 parent = NULL;
445                 /*
446                  * Make sure the last processed component was a directory. The
447                  * trailing slashes are illegal behind anything else.
448                  */
449                 if (!(err ||
450                       S_ISDIR(nd->nd_pno->p_base->pb_ino->i_stbuf.st_mode)))
451                         err = -ENOTDIR;
452         }
453
454         /*
455          * Drop reference to parent if set. Either we have a dup of the original
456          * parent or an intermediate reference.
457          */
458         if (parent)
459                 P_RELE(parent);
460
461         /*
462          * On error, we will want to drop our reference to the current
463          * path node if at end.
464          */
465         if (err && nd->nd_pno) {
466                 P_RELE(nd->nd_pno);
467                 nd->nd_pno = NULL;
468         }
469
470         return err;
471 }
472
473 #ifdef CPLANT_YOD
474 /* 
475  * for backward compatibility w/protocol switch
476  * remove everything up to the first ':'
477  * fortran libs prepend cwd to path, so not much choice
478  */
479 #define STRIP_PREFIX(p) strchr(p,':') ? strchr(p,':')+1 : p
480 #else
481 #define STRIP_PREFIX(p) p
482 #endif
483
484 /*
485  * Expanded form of the path-walk routine, with the common arguments, builds
486  * the nameidata bundle and calls path-walk.
487  */
488 int
489 _sysio_namei(struct pnode *parent,
490              const char *path,
491              unsigned flags,
492              struct intent *intnt,
493              struct pnode **pnop)
494 {
495         struct nameidata nameidata;
496         int     err;
497
498         ND_INIT(&nameidata, flags, STRIP_PREFIX(path), _sysio_root, intnt);
499         err = _sysio_path_walk(parent, &nameidata);
500         if (!err)
501                 *pnop = nameidata.nd_pno;
502         return err;
503 }