OSDN Git Service

Merge tag 'v4.4.214' into 10
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / fs / namei.c
index 9071bb1..e221fed 100644 (file)
@@ -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;
@@ -1003,7 +1087,8 @@ static int may_linkat(struct path *link)
  * may_create_in_sticky - Check whether an O_CREAT open in a sticky directory
  *                       should be allowed, or not, on files that already
  *                       exist.
- * @dir: the sticky parent directory
+ * @dir_mode: mode bits of directory
+ * @dir_uid: owner of directory
  * @inode: the inode of the file to open
  *
  * Block an O_CREAT open of a FIFO (or a regular file) when:
@@ -1019,18 +1104,18 @@ static int may_linkat(struct path *link)
  *
  * Returns 0 if the open is allowed, -ve on error.
  */
-static int may_create_in_sticky(struct dentry * const dir,
+static int may_create_in_sticky(umode_t dir_mode, kuid_t dir_uid,
                                struct inode * const inode)
 {
        if ((!sysctl_protected_fifos && S_ISFIFO(inode->i_mode)) ||
            (!sysctl_protected_regular && S_ISREG(inode->i_mode)) ||
-           likely(!(dir->d_inode->i_mode & S_ISVTX)) ||
-           uid_eq(inode->i_uid, dir->d_inode->i_uid) ||
+           likely(!(dir_mode & S_ISVTX)) ||
+           uid_eq(inode->i_uid, dir_uid) ||
            uid_eq(current_fsuid(), inode->i_uid))
                return 0;
 
-       if (likely(dir->d_inode->i_mode & 0002) ||
-           (dir->d_inode->i_mode & 0020 &&
+       if (likely(dir_mode & 0002) ||
+           (dir_mode & 0020 &&
             ((sysctl_protected_fifos >= 2 && S_ISFIFO(inode->i_mode)) ||
              (sysctl_protected_regular >= 2 && S_ISREG(inode->i_mode))))) {
                return -EACCES;
@@ -2205,6 +2290,7 @@ static int path_lookupat(struct nameidata *nd, unsigned flags, struct path *path
        if (!err && nd->flags & LOOKUP_DIRECTORY)
                if (!d_can_lookup(nd->path.dentry))
                        err = -ENOTDIR;
+
        if (!err) {
                *path = nd->path;
                nd->path.mnt = NULL;
@@ -2733,8 +2819,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);
@@ -3091,6 +3183,8 @@ static int do_last(struct nameidata *nd,
                   int *opened)
 {
        struct dentry *dir = nd->path.dentry;
+       kuid_t dir_uid = nd->inode->i_uid;
+       umode_t dir_mode = nd->inode->i_mode;
        int open_flag = op->open_flag;
        bool will_truncate = (open_flag & O_TRUNC) != 0;
        bool got_write = false;
@@ -3243,7 +3337,7 @@ finish_open:
                error = -EISDIR;
                if (d_is_dir(nd->path.dentry))
                        goto out;
-               error = may_create_in_sticky(dir,
+               error = may_create_in_sticky(dir_mode, dir_uid,
                                             d_backing_inode(nd->path.dentry));
                if (unlikely(error))
                        goto out;
@@ -3425,6 +3519,8 @@ out2:
                                error = -ESTALE;
                }
                file = ERR_PTR(error);
+       } else {
+               global_filetable_add(file);
        }
        return file;
 }
@@ -3592,8 +3688,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);