Merge branch 'for-6.9/amd-sfh' into for-linus
[sfrench/cifs-2.6.git] / fs / kernfs / dir.c
index 8b2bd65d70e7257022f139d7755d650a2f4b3ac6..bce1d7ac95caaa6ae48ba62c094d43c9da27298e 100644 (file)
@@ -54,9 +54,9 @@ static bool kernfs_lockdep(struct kernfs_node *kn)
 static int kernfs_name_locked(struct kernfs_node *kn, char *buf, size_t buflen)
 {
        if (!kn)
-               return strlcpy(buf, "(null)", buflen);
+               return strscpy(buf, "(null)", buflen);
 
-       return strlcpy(buf, kn->parent ? kn->name : "/", buflen);
+       return strscpy(buf, kn->parent ? kn->name : "/", buflen);
 }
 
 /* kernfs_node_depth - compute depth from @from to @to */
@@ -127,7 +127,7 @@ static struct kernfs_node *kernfs_common_ancestor(struct kernfs_node *a,
  *
  * [3] when @kn_to is %NULL result will be "(null)"
  *
- * Return: the length of the full path.  If the full length is equal to or
+ * Return: the length of the constructed path.  If the path would have been
  * greater than @buflen, @buf contains the truncated path with the trailing
  * '\0'.  On error, -errno is returned.
  */
@@ -138,16 +138,17 @@ static int kernfs_path_from_node_locked(struct kernfs_node *kn_to,
        struct kernfs_node *kn, *common;
        const char parent_str[] = "/..";
        size_t depth_from, depth_to, len = 0;
+       ssize_t copied;
        int i, j;
 
        if (!kn_to)
-               return strlcpy(buf, "(null)", buflen);
+               return strscpy(buf, "(null)", buflen);
 
        if (!kn_from)
                kn_from = kernfs_root(kn_to)->kn;
 
        if (kn_from == kn_to)
-               return strlcpy(buf, "/", buflen);
+               return strscpy(buf, "/", buflen);
 
        common = kernfs_common_ancestor(kn_from, kn_to);
        if (WARN_ON(!common))
@@ -158,18 +159,19 @@ static int kernfs_path_from_node_locked(struct kernfs_node *kn_to,
 
        buf[0] = '\0';
 
-       for (i = 0; i < depth_from; i++)
-               len += strlcpy(buf + len, parent_str,
-                              len < buflen ? buflen - len : 0);
+       for (i = 0; i < depth_from; i++) {
+               copied = strscpy(buf + len, parent_str, buflen - len);
+               if (copied < 0)
+                       return copied;
+               len += copied;
+       }
 
        /* Calculate how many bytes we need for the rest */
        for (i = depth_to - 1; i >= 0; i--) {
                for (kn = kn_to, j = 0; j < i; j++)
                        kn = kn->parent;
-               len += strlcpy(buf + len, "/",
-                              len < buflen ? buflen - len : 0);
-               len += strlcpy(buf + len, kn->name,
-                              len < buflen ? buflen - len : 0);
+
+               len += scnprintf(buf + len, buflen - len, "/%s", kn->name);
        }
 
        return len;
@@ -182,12 +184,12 @@ static int kernfs_path_from_node_locked(struct kernfs_node *kn_to,
  * @buflen: size of @buf
  *
  * Copies the name of @kn into @buf of @buflen bytes.  The behavior is
- * similar to strlcpy().
+ * similar to strscpy().
  *
  * Fills buffer with "(null)" if @kn is %NULL.
  *
- * Return: the length of @kn's name and if @buf isn't long enough,
- * it's filled up to @buflen-1 and nul terminated.
+ * Return: the resulting length of @buf. If @buf isn't long enough,
+ * it's filled up to @buflen-1 and nul terminated, and returns -E2BIG.
  *
  * This function can be called from any context.
  */
@@ -214,7 +216,7 @@ int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen)
  * path (which includes '..'s) as needed to reach from @from to @to is
  * returned.
  *
- * Return: the length of the full path.  If the full length is equal to or
+ * Return: the length of the constructed path.  If the path would have been
  * greater than @buflen, @buf contains the truncated path with the trailing
  * '\0'.  On error, -errno is returned.
  */
@@ -265,12 +267,10 @@ void pr_cont_kernfs_path(struct kernfs_node *kn)
        sz = kernfs_path_from_node(kn, NULL, kernfs_pr_cont_buf,
                                   sizeof(kernfs_pr_cont_buf));
        if (sz < 0) {
-               pr_cont("(error)");
-               goto out;
-       }
-
-       if (sz >= sizeof(kernfs_pr_cont_buf)) {
-               pr_cont("(name too long)");
+               if (sz == -E2BIG)
+                       pr_cont("(name too long)");
+               else
+                       pr_cont("(error)");
                goto out;
        }
 
@@ -676,6 +676,18 @@ struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
 {
        struct kernfs_node *kn;
 
+       if (parent->mode & S_ISGID) {
+               /* this code block imitates inode_init_owner() for
+                * kernfs
+                */
+
+               if (parent->iattr)
+                       gid = parent->iattr->ia_gid;
+
+               if (flags & KERNFS_DIR)
+                       mode |= S_ISGID;
+       }
+
        kn = __kernfs_new_node(kernfs_root(parent), parent,
                               name, mode, uid, gid, flags);
        if (kn) {
@@ -850,16 +862,16 @@ static struct kernfs_node *kernfs_walk_ns(struct kernfs_node *parent,
                                          const unsigned char *path,
                                          const void *ns)
 {
-       size_t len;
+       ssize_t len;
        char *p, *name;
 
        lockdep_assert_held_read(&kernfs_root(parent)->kernfs_rwsem);
 
        spin_lock_irq(&kernfs_pr_cont_lock);
 
-       len = strlcpy(kernfs_pr_cont_buf, path, sizeof(kernfs_pr_cont_buf));
+       len = strscpy(kernfs_pr_cont_buf, path, sizeof(kernfs_pr_cont_buf));
 
-       if (len >= sizeof(kernfs_pr_cont_buf)) {
+       if (len < 0) {
                spin_unlock_irq(&kernfs_pr_cont_lock);
                return NULL;
        }