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