• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 Institute of Parallel And Distributed Systems (IPADS), Shanghai Jiao Tong University (SJTU)
3  * Licensed under the Mulan PSL v2.
4  * You can use this software according to the terms and conditions of the Mulan PSL v2.
5  * You may obtain a copy of Mulan PSL v2 at:
6  *     http://license.coscl.org.cn/MulanPSL2
7  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
8  * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
9  * PURPOSE.
10  * See the Mulan PSL v2 for more details.
11  */
12 /*
13  * This file contains utils of pathname resolution for tmpfs.
14  *
15  * a path: "/this/is/a/path"
16  * a component: "this", "is", etc.
17  *
18  * In general, a path can be divided into two parts, and we deal with each
19  * part separately. One part is the "final component" part, "path" in the above
20  * example. The other part is the "everything else" part, "/this/is/a" in the
21  * above example.
22  * The reason for such separation is that the final component
23  * usually requires some special care, for example in a call to open() syscall,
24  * whether or not to create the file if it's missing.
25  * The every thing else part is much easier, since we always follow
26  * intermediate symlink when we can, and the everything else part should
27  * only resolve to existing directories.
28  *
29  * The way tmpfs' pathname resolution works is as follows:
30  * 0. walk_component() finds a dentry for one component each time, and update
31  *    nd->current by calling step_into() so we get one more level
32  *    deeper in the directory tree. It also deals with symlinks and
33  *    some other special cases.
34  *
35  * 1. We call walk_prefix() to do a general purpose pathname lookup on the
36  *    everything else part. The function will update nd->last in each of its
37  *    loop to process one component in the pathname, then call
38  *    walk_component() to actually find the dentry of that component name,
39  *    in which nd->current will be updated. walk_prefix() only go as far as
40  *    setting nd->last to the final component and nd->current to the
41  *    parent directory entry of that final component.
42  *
43  * 2. Based on the requirements of different FS system calls, we may take
44  *    different approaches to deal with the final component, for example create
45  *    it when it's missing. So we wrap this functionality into three wrapper
46  *    functions path_parentat(), path_lookupat(), path_openat(). The interfaces
47  *    of tmpfs will call these wrappers to do a full pathname lookup and deal
48  *    with corner cases.
49  */
50 
51 #include "namei.h"
52 #include "chcore/error.h"
53 #include "defs.h"
54 #include "limits.h"
55 #include "stddef.h"
56 #include <errno.h>
57 #include "fcntl.h"
58 #include <string.h>
59 
60 /**
61  * @brief length until next '/' or '\0'
62  * @param name pointer to a null terminated string
63  * @return u64 The length of the first component pointed to by name
64  */
get_component_len(const char * name)65 static inline u64 get_component_len(const char *name)
66 {
67     int i = 0;
68     while (name[i] != '\0' && name[i] != '/') {
69         i++;
70     }
71     return i;
72 }
73 
init_nd(struct nameidata * nd,unsigned flags)74 static void init_nd(struct nameidata *nd, unsigned flags)
75 {
76     nd->depth = 0;
77     nd->flags = flags;
78     /*
79      * The current implementation of fs_base ensures tmpfs only get absolute
80      * path when dealing with file system requests
81      */
82     nd->current = tmpfs_root_dent;
83     nd->total_link_count = 0;
84     nd->last.str = NULL;
85 }
86 
87 /**
88  * @brief Decide whether we can follow the symlink.
89  * @param nd The nameidata structure to check.
90  * @return int 0 if no error, or errno.
91  */
can_follow_symlink(struct nameidata * nd)92 static int can_follow_symlink(struct nameidata *nd)
93 {
94     /* No stack space for the new symlink */
95     if (nd->depth == MAX_STACK_SIZE) {
96         return -ENOMEM;
97     }
98 
99     /* Too many symlink encountered */
100     if (nd->total_link_count++ == MAX_SYM_CNT) {
101         return -ELOOP;
102     }
103 
104     return 0;
105 }
106 
107 /**
108  * @brief update nd->current according to the found dentry
109  * @param nd The nd structure of this path lookup.
110  * @param trailing Indicate whether this is a trailing component, if it is, it
111  * requires some special care.
112  * @param dentry The dentry for nd->current to update to.
113  * @return const char* Return NULL if not a symlink or should not follow, return
114  * the symlink otherwise.
115  */
step_into(struct nameidata * nd,bool trailing,struct dentry * dentry)116 static const char *step_into(struct nameidata *nd, bool trailing,
117                              struct dentry *dentry)
118 {
119     int err;
120 
121     /*
122      * The dentry is not a symlink or should not be followed
123      */
124     if (dentry->inode->type != FS_SYM
125         /* only the trailing symlink needs to check if it can be followed.
126            always follow non-trailing symlinks */
127         || (trailing && !(nd->flags & ND_FOLLOW))) {
128         nd->current = dentry;
129         return NULL;
130     }
131 
132     /* dealing with a symlink now */
133 
134     if ((err = can_follow_symlink(nd))) {
135         return CHCORE_ERR_PTR(err);
136     }
137 
138     /* symlink is not allowed */
139     if (nd->flags & ND_NO_SYMLINKS) {
140         return CHCORE_ERR_PTR(-ELOOP);
141     }
142 
143     /*
144      * we directly return the actual symlink,
145      * without copying it
146      */
147     const char *name = dentry->inode->symlink;
148 
149     if (!name) {
150         return NULL;
151     }
152 
153     if (*name == '/') {
154         /* Absolute path symlink, jump to root */
155         nd->current = tmpfs_root_dent;
156 
157         do {
158             name++;
159         } while (*name == '/');
160     }
161 
162     /* we do not update nd->current if the symlink is a relative path. */
163 
164     return *name ? name : NULL;
165 }
166 
167 /**
168  * @brief Lookup a single component named "nd->last" under the dentry
169  * "nd->current". Find the dentry in walk_component() then call step_into() to
170  * process symlinks and some flags.
171  * @param nd The nd structure representing this path lookup.
172  * @param trailing If this is the trailing component, some special care would be
173  * taken when considering symlinks.
174  * @return const char* Return NULL if no symlink encountered or should/can not
175  * follow, return the symlink to follow it.
176  * @note The result of this function during a pathname lookup will be an updated
177  * nd->current, serving as the parent directory of next component lookup.
178  */
walk_component(struct nameidata * nd,bool trailing)179 static const char *walk_component(struct nameidata *nd, bool trailing)
180 {
181     struct dentry *dentry;
182     struct inode *i_parent;
183 
184     i_parent = nd->current->inode;
185     if (i_parent->type != FS_DIR) {
186         return CHCORE_ERR_PTR(-ENOTDIR);
187     }
188 
189     /* Find the dentry of the nd->last component under nd->current */
190     dentry =
191         i_parent->d_ops->dirlookup(i_parent, nd->last.str, (int)nd->last.len);
192 
193     if (dentry == NULL) {
194         return CHCORE_ERR_PTR(-ENOENT); /* File not exist */
195     }
196 
197     return step_into(nd, trailing, dentry);
198 };
199 
200 /**
201  * @brief A simple wrapper of walk_component, used when looking up the last
202  * component in the path.
203  * @param nd The nd structure representing this path lookup.
204  * @return const char* Return NULL if no symlink encountered or should/can not
205  * follow, return the symlink to follow it.
206  */
lookup_last(struct nameidata * nd)207 static inline const char *lookup_last(struct nameidata *nd)
208 {
209     if (nd->last.str == NULL) {
210         return NULL;
211     }
212 
213     return walk_component(nd, true);
214 }
215 
216 /**
217  * @brief lookup the dentry that is to be opened, if it exists, return it.
218  * If it does not exist and O_CREAT is set, creat a regular file of the name
219  * @param nd The nd structure representing this path lookup.
220  * @param open_flags The open flags of this open() syscall.
221  * @return struct dentry* Return the found/created dentry, or NULL if not found.
222  */
lookup_create(struct nameidata * nd,unsigned open_flags)223 static struct dentry *lookup_create(struct nameidata *nd, unsigned open_flags)
224 {
225     struct dentry *dir = nd->current;
226     struct inode *i_dir = dir->inode;
227     struct dentry *dentry;
228     int err;
229 
230     dentry = i_dir->d_ops->dirlookup(i_dir, nd->last.str, (int)nd->last.len);
231     if (dentry) {
232         return dentry;
233     }
234 
235     /* not found, create it */
236     if (open_flags & O_CREAT) {
237         dentry = i_dir->d_ops->alloc_dentry();
238         if (CHCORE_IS_ERR(dentry)) {
239             return dentry;
240         }
241 
242         err =
243             i_dir->d_ops->add_dentry(i_dir, dentry, nd->last.str, nd->last.len);
244         if (err) {
245             i_dir->d_ops->free_dentry(dentry);
246             return CHCORE_ERR_PTR(err);
247         }
248 
249         /* we are not currently handling mode in open() */
250         mode_t faked_mode = 0x888;
251 
252         int err = i_dir->d_ops->mknod(i_dir, dentry, faked_mode, FS_REG);
253         if (err) {
254             i_dir->d_ops->remove_dentry(i_dir, dentry);
255             i_dir->d_ops->free_dentry(dentry);
256             return CHCORE_ERR_PTR(err);
257         }
258     }
259 
260     return dentry;
261 }
262 
263 /**
264  * @brief Used in open when looking up the last component of a path, may create
265  * the file if not found.
266  * @param nd The nd structure representing this path lookup.
267  * @param open_flags The open flags of this open() syscall.
268  * @return const char* Return NULL if no symlink encountered or should/can not
269  * follow, return the symlink to follow it.
270  */
lookup_last_open(struct nameidata * nd,unsigned open_flags)271 static const char *lookup_last_open(struct nameidata *nd, unsigned open_flags)
272 {
273     struct dentry *dentry;
274 
275     if (!nd->last.str) {
276         return NULL;
277     }
278 
279     if ((open_flags & O_CREAT)) {
280         /*
281          * when O_CREAT is set
282          * the path should not have trailing slashes
283          */
284         if (nd->flags & ND_TRAILING_SLASH) {
285             return CHCORE_ERR_PTR(-EISDIR);
286         }
287     }
288 
289     dentry = lookup_create(nd, open_flags);
290 
291     if (!dentry) {
292         return CHCORE_ERR_PTR(-ENOENT);
293     }
294 
295     if (CHCORE_IS_ERR(dentry)) {
296         return (char *)dentry;
297     }
298 
299     return step_into(nd, true, dentry);
300 }
301 
302 /**
303  * @brief lookup a path except its final component
304  * @param name The full pathname to lookup.
305  * @param nd The nd structure to represent this pathname lookup and to store
306  * state information of this lookup.
307  * @return int 0 on success, errno on failure.
308  */
walk_prefix(const char * name,struct nameidata * nd)309 static int walk_prefix(const char *name, struct nameidata *nd)
310 {
311     int err;
312     if (CHCORE_IS_ERR(name)) {
313         return CHCORE_PTR_ERR(name);
314     }
315 
316     while (*name == '/') {
317         name++;
318     }
319 
320     if (!*name) {
321         return 0;
322     }
323 
324     /* each loop deals with one next path component or get a new symlink */
325     for (;;) {
326         const char *link;
327         u64 component_len = get_component_len(name);
328 
329         if (component_len > NAME_MAX) {
330             return -ENAMETOOLONG;
331         }
332 
333         err = init_string(&nd->last, name, component_len);
334         if (err) {
335             return err;
336         }
337 
338         name += component_len;
339         /* skipping postfixing '/'s till next component name */
340         while (*name == '/') {
341             name++;
342         }
343 
344         if (!*name) {
345             if (!nd->depth)
346                 /* this is the trailing component */
347                 return 0;
348 
349             /* pop a link, continue processing */
350             name = nd->stack[--nd->depth];
351         }
352 
353         link = walk_component(nd, false);
354 
355         /* we have another symlink to process */
356         if (link) {
357             if (CHCORE_IS_ERR(link)) {
358                 return CHCORE_PTR_ERR(link);
359             }
360 
361             /* checked in step_into() that we have space on stack */
362             nd->stack[nd->depth++] = name; /* store current name */
363             name = link; /* deal with the symlink first */
364             continue;
365         }
366 
367         /* next loop requires nd->current to be a directory */
368         if (nd->current->inode->type != FS_DIR) {
369             return -ENOTDIR;
370         }
371     }
372     return 0;
373 }
374 
375 /**
376  * @brief Find the parent directory of a given pathname. A very simple wrapper
377  * of walk_prefix(). Used by rename(), unlink(), etc.
378  * @param nd The nd structure representing this lookup.
379  * @param path The full path to lookup.
380  * @param flags Some restriction of this lookup can be passed by the flags
381  * param.
382  * @return int 0 on success, errno on failure.
383  * @return struct dentry* Returned by pointer, the parent directory's
384  * dentry
385  * @return char* Returned in nd->last, the name of the last component of the
386  * pathname.
387  * @note We **DO NOT** call free_string() here because it is normal
388  * for the caller to use nd->last after calling path_parentat() (to
389  * create/rename/remove it under the parent directory). It should be viewed as
390  * the return value of this call.
391  */
path_parentat(struct nameidata * nd,const char * path,unsigned flags,struct dentry ** parent)392 int path_parentat(struct nameidata *nd, const char *path, unsigned flags,
393                   struct dentry **parent)
394 {
395     init_nd(nd, flags);
396 
397     /*
398      * there's no need to do the checking of trailing slashes here,
399      * since path_parentat never cares about the final component.
400      */
401 
402     int err = walk_prefix(path, nd);
403     if (!err) {
404         *parent = nd->current;
405     }
406 
407     /*
408      * we **do not** call free_string() here because it is normal for the
409      * caller to use nd->last after calling path_parentat()
410      *
411      * one should also view nd->last as an output of path_parentat()
412      * the subtlety here is annoying but at least we made it clear...
413      */
414     return err;
415 }
416 
417 /**
418  * @brief Get the dentry of the full path. Used by: stat(), chmod(), etc.
419  * @param nd The nd structure representing this lookup.
420  * @param path The full path to lookup.
421  * @param flags Some restriction of this lookup can be passed by the flags
422  * param.
423  * @return int 0 on success, errno on failure.
424  * @return struct dentry* Returned by pointer, the final component's
425  * dentry.
426  * @note We call free_string() here because the caller should never use
427  * nd->last after calling path_lookupat().
428  */
path_lookupat(struct nameidata * nd,const char * path,unsigned flags,struct dentry ** dentry)429 int path_lookupat(struct nameidata *nd, const char *path, unsigned flags,
430                   struct dentry **dentry)
431 {
432     int err;
433     init_nd(nd, flags);
434 
435     if (path[strlen(path) - 1] == '/') {
436         nd->flags |= ND_TRAILING_SLASH | ND_DIRECTORY | ND_FOLLOW;
437     }
438 
439     while (!(err = walk_prefix(path, nd)) && (path = lookup_last(nd)) != NULL) {
440         ;
441     }
442 
443     /* requiring a directory(because of trailing slashes) */
444     if (!err && (nd->flags & ND_DIRECTORY)
445         && nd->current->inode->type != FS_DIR) {
446         err = -ENOTDIR;
447     }
448 
449     if (!err) {
450         *dentry = nd->current;
451     }
452 
453     /*
454      * we call free_string() here because the caller should never use
455      * nd->last after calling path_lookupat()
456      *
457      * in other words, the only effective output of path_lookupat
458      * is *dentry when no err is encountered.
459      */
460     free_string(&nd->last);
461     return err;
462 }
463 
464 /**
465  * @brief Called by open(), behaviour is determined by open_flags.
466  * We lookup the path, and do special handling of open().
467  * @param nd The nd structure representing this lookup.
468  * @param path The full path to lookup.
469  * @param open_flags The open flags of the open() syscall.
470  * @param flags Some restriction of this lookup can be passed by the flags
471  * param.
472  * @return int 0 on success, errno on failure.
473  * @return struct dentry* Returned by pointer, NULL if not found and cannot be
474  * created, or the found/created dentry.
475  * @note We call free_string() here because the caller should never use
476  * nd->last after calling path_openat().
477  *
478  */
path_openat(struct nameidata * nd,const char * path,unsigned open_flags,unsigned flags,struct dentry ** dentry)479 int path_openat(struct nameidata *nd, const char *path, unsigned open_flags,
480                 unsigned flags, struct dentry **dentry)
481 {
482     int err;
483 
484     init_nd(nd, flags);
485 
486     if (path[strlen(path) - 1] == '/') {
487         nd->flags |= ND_TRAILING_SLASH | ND_DIRECTORY | ND_FOLLOW;
488     }
489 
490     /* we don't follow symlinks at end by default */
491     if (!(open_flags & O_NOFOLLOW)) {
492         nd->flags |= ND_FOLLOW;
493     }
494 
495     if (open_flags & O_DIRECTORY) {
496         nd->flags |= ND_DIRECTORY;
497     }
498 
499     while (!(err = walk_prefix(path, nd))
500            && (path = lookup_last_open(nd, open_flags)) != NULL) {
501         ;
502     }
503 
504     if (!err) {
505         struct inode *inode = nd->current->inode;
506 
507         /* we can check O_CREAT | O_EXCL here, but fs_base handles it */
508 
509         if ((open_flags & O_CREAT) && (inode->type == FS_DIR)) {
510             err = -EISDIR;
511             goto error;
512         }
513 
514         if ((nd->flags & ND_DIRECTORY) && !(inode->type == FS_DIR)) {
515             err = -ENOTDIR;
516             goto error;
517         }
518 
519         /* we can check O_TRUNCATE here, but fs_base handles it */
520 
521         *dentry = nd->current;
522     }
523 
524 error:
525     /*
526      * we call free_string() here because the caller should never use
527      * nd->last after calling path_openat(). the reason is the same
528      * as explained in path_lookupat()
529      */
530     free_string(&nd->last);
531     return err;
532 }