diff options
Diffstat (limited to 'fs/namei.c')
| -rw-r--r-- | fs/namei.c | 106 |
1 files changed, 103 insertions, 3 deletions
diff --git a/fs/namei.c b/fs/namei.c index 3a8f1327149f..fe1612ac009d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -40,6 +40,9 @@ #include "internal.h" #include "mount.h" +#define CREATE_TRACE_POINTS +#include <trace/events/namei.h> + /* [Feb-1997 T. Schoebel-Theuer] * Fundamental changes in the pathname lookup mechanisms (namei) * were necessary because of omirr. The reason is that omirr needs @@ -784,6 +787,81 @@ static inline int d_revalidate(struct dentry *dentry, unsigned int flags) return dentry->d_op->d_revalidate(dentry, flags); } +#define INIT_PATH_SIZE 64 + +static void success_walk_trace(struct nameidata *nd) +{ + struct path *pt = &nd->path; + struct inode *i = nd->inode; + char buf[INIT_PATH_SIZE], *try_buf; + int cur_path_size; + char *p; + + /* When eBPF/ tracepoint is disabled, keep overhead low. */ + if (!trace_inodepath_enabled()) + return; + + /* First try stack allocated buffer. */ + try_buf = buf; + cur_path_size = INIT_PATH_SIZE; + + while (cur_path_size <= PATH_MAX) { + /* Free previous heap allocation if we are now trying + * a second or later heap allocation. + */ + if (try_buf != buf) + kfree(try_buf); + + /* All but the first alloc are on the heap. */ + if (cur_path_size != INIT_PATH_SIZE) { + try_buf = kmalloc(cur_path_size, GFP_KERNEL); + if (!try_buf) { + try_buf = buf; + sprintf(try_buf, "error:buf_alloc_failed"); + break; + } + } + + p = d_path(pt, try_buf, cur_path_size); + + if (!IS_ERR(p)) { + char *end = mangle_path(try_buf, p, "\n"); + + if (end) { + try_buf[end - try_buf] = 0; + break; + } else { + /* On mangle errors, double path size + * till PATH_MAX. + */ + cur_path_size = cur_path_size << 1; + continue; + } + } + + if (PTR_ERR(p) == -ENAMETOOLONG) { + /* If d_path complains that name is too long, + * then double path size till PATH_MAX. + */ + cur_path_size = cur_path_size << 1; + continue; + } + + sprintf(try_buf, "error:d_path_failed_%lu", + -1 * PTR_ERR(p)); + break; + } + + if (cur_path_size > PATH_MAX) + sprintf(try_buf, "error:d_path_name_too_long"); + + trace_inodepath(i, try_buf); + + if (try_buf != buf) + kfree(try_buf); + return; +} + /** * complete_walk - successful completion of path walk * @nd: pointer nameidata @@ -806,15 +884,21 @@ static int complete_walk(struct nameidata *nd) return -ECHILD; } - if (likely(!(nd->flags & LOOKUP_JUMPED))) + if (likely(!(nd->flags & LOOKUP_JUMPED))) { + success_walk_trace(nd); return 0; + } - if (likely(!(dentry->d_flags & DCACHE_OP_WEAK_REVALIDATE))) + if (likely(!(dentry->d_flags & DCACHE_OP_WEAK_REVALIDATE))) { + success_walk_trace(nd); return 0; + } status = dentry->d_op->d_weak_revalidate(dentry, nd->flags); - if (status > 0) + if (status > 0) { + success_walk_trace(nd); return 0; + } if (!status) status = -ESTALE; @@ -2734,8 +2818,14 @@ int vfs_create2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, if (error) return error; error = dir->i_op->create(dir, dentry, mode, want_excl); + if (error) + return error; + error = security_inode_post_create(dir, dentry, mode); + if (error) + return error; if (!error) fsnotify_create(dir, dentry); + return error; } EXPORT_SYMBOL(vfs_create2); @@ -3428,6 +3518,8 @@ out2: error = -ESTALE; } file = ERR_PTR(error); + } else { + global_filetable_add(file); } return file; } @@ -3595,8 +3687,16 @@ int vfs_mknod2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, u return error; error = dir->i_op->mknod(dir, dentry, mode, dev); + if (error) + return error; + + error = security_inode_post_create(dir, dentry, mode); + if (error) + return error; + if (!error) fsnotify_create(dir, dentry); + return error; } EXPORT_SYMBOL(vfs_mknod2); |
