root/fs/configfs/symlink.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. item_depth
  2. item_path_length
  3. fill_item_path
  4. configfs_get_target_path
  5. create_link
  6. get_target
  7. configfs_symlink
  8. configfs_unlink

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /* -*- mode: c; c-basic-offset: 8; -*-
   3  * vim: noexpandtab sw=8 ts=8 sts=0:
   4  *
   5  * symlink.c - operations for configfs symlinks.
   6  *
   7  * Based on sysfs:
   8  *      sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel
   9  *
  10  * configfs Copyright (C) 2005 Oracle.  All rights reserved.
  11  */
  12 
  13 #include <linux/fs.h>
  14 #include <linux/module.h>
  15 #include <linux/namei.h>
  16 #include <linux/slab.h>
  17 
  18 #include <linux/configfs.h>
  19 #include "configfs_internal.h"
  20 
  21 /* Protects attachments of new symlinks */
  22 DEFINE_MUTEX(configfs_symlink_mutex);
  23 
  24 static int item_depth(struct config_item * item)
  25 {
  26         struct config_item * p = item;
  27         int depth = 0;
  28         do { depth++; } while ((p = p->ci_parent) && !configfs_is_root(p));
  29         return depth;
  30 }
  31 
  32 static int item_path_length(struct config_item * item)
  33 {
  34         struct config_item * p = item;
  35         int length = 1;
  36         do {
  37                 length += strlen(config_item_name(p)) + 1;
  38                 p = p->ci_parent;
  39         } while (p && !configfs_is_root(p));
  40         return length;
  41 }
  42 
  43 static void fill_item_path(struct config_item * item, char * buffer, int length)
  44 {
  45         struct config_item * p;
  46 
  47         --length;
  48         for (p = item; p && !configfs_is_root(p); p = p->ci_parent) {
  49                 int cur = strlen(config_item_name(p));
  50 
  51                 /* back up enough to print this bus id with '/' */
  52                 length -= cur;
  53                 memcpy(buffer + length, config_item_name(p), cur);
  54                 *(buffer + --length) = '/';
  55         }
  56 }
  57 
  58 static int configfs_get_target_path(struct config_item *item,
  59                 struct config_item *target, char *path)
  60 {
  61         int depth, size;
  62         char *s;
  63 
  64         depth = item_depth(item);
  65         size = item_path_length(target) + depth * 3 - 1;
  66         if (size > PATH_MAX)
  67                 return -ENAMETOOLONG;
  68 
  69         pr_debug("%s: depth = %d, size = %d\n", __func__, depth, size);
  70 
  71         for (s = path; depth--; s += 3)
  72                 strcpy(s,"../");
  73 
  74         fill_item_path(target, path, size);
  75         pr_debug("%s: path = '%s'\n", __func__, path);
  76         return 0;
  77 }
  78 
  79 static int create_link(struct config_item *parent_item,
  80                        struct config_item *item,
  81                        struct dentry *dentry)
  82 {
  83         struct configfs_dirent *target_sd = item->ci_dentry->d_fsdata;
  84         char *body;
  85         int ret;
  86 
  87         if (!configfs_dirent_is_ready(target_sd))
  88                 return -ENOENT;
  89 
  90         body = kzalloc(PAGE_SIZE, GFP_KERNEL);
  91         if (!body)
  92                 return -ENOMEM;
  93 
  94         configfs_get(target_sd);
  95         spin_lock(&configfs_dirent_lock);
  96         if (target_sd->s_type & CONFIGFS_USET_DROPPING) {
  97                 spin_unlock(&configfs_dirent_lock);
  98                 configfs_put(target_sd);
  99                 kfree(body);
 100                 return -ENOENT;
 101         }
 102         target_sd->s_links++;
 103         spin_unlock(&configfs_dirent_lock);
 104         ret = configfs_get_target_path(parent_item, item, body);
 105         if (!ret)
 106                 ret = configfs_create_link(target_sd, parent_item->ci_dentry,
 107                                            dentry, body);
 108         if (ret) {
 109                 spin_lock(&configfs_dirent_lock);
 110                 target_sd->s_links--;
 111                 spin_unlock(&configfs_dirent_lock);
 112                 configfs_put(target_sd);
 113                 kfree(body);
 114         }
 115         return ret;
 116 }
 117 
 118 
 119 static int get_target(const char *symname, struct path *path,
 120                       struct config_item **target, struct super_block *sb)
 121 {
 122         int ret;
 123 
 124         ret = kern_path(symname, LOOKUP_FOLLOW|LOOKUP_DIRECTORY, path);
 125         if (!ret) {
 126                 if (path->dentry->d_sb == sb) {
 127                         *target = configfs_get_config_item(path->dentry);
 128                         if (!*target) {
 129                                 ret = -ENOENT;
 130                                 path_put(path);
 131                         }
 132                 } else {
 133                         ret = -EPERM;
 134                         path_put(path);
 135                 }
 136         }
 137 
 138         return ret;
 139 }
 140 
 141 
 142 int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
 143 {
 144         int ret;
 145         struct path path;
 146         struct configfs_dirent *sd;
 147         struct config_item *parent_item;
 148         struct config_item *target_item = NULL;
 149         const struct config_item_type *type;
 150 
 151         sd = dentry->d_parent->d_fsdata;
 152         /*
 153          * Fake invisibility if dir belongs to a group/default groups hierarchy
 154          * being attached
 155          */
 156         if (!configfs_dirent_is_ready(sd))
 157                 return -ENOENT;
 158 
 159         parent_item = configfs_get_config_item(dentry->d_parent);
 160         type = parent_item->ci_type;
 161 
 162         ret = -EPERM;
 163         if (!type || !type->ct_item_ops ||
 164             !type->ct_item_ops->allow_link)
 165                 goto out_put;
 166 
 167         /*
 168          * This is really sick.  What they wanted was a hybrid of
 169          * link(2) and symlink(2) - they wanted the target resolved
 170          * at syscall time (as link(2) would've done), be a directory
 171          * (which link(2) would've refused to do) *AND* be a deep
 172          * fucking magic, making the target busy from rmdir POV.
 173          * symlink(2) is nothing of that sort, and the locking it
 174          * gets matches the normal symlink(2) semantics.  Without
 175          * attempts to resolve the target (which might very well
 176          * not even exist yet) done prior to locking the parent
 177          * directory.  This perversion, OTOH, needs to resolve
 178          * the target, which would lead to obvious deadlocks if
 179          * attempted with any directories locked.
 180          *
 181          * Unfortunately, that garbage is userland ABI and we should've
 182          * said "no" back in 2005.  Too late now, so we get to
 183          * play very ugly games with locking.
 184          *
 185          * Try *ANYTHING* of that sort in new code, and you will
 186          * really regret it.  Just ask yourself - what could a BOFH
 187          * do to me and do I want to find it out first-hand?
 188          *
 189          *  AV, a thoroughly annoyed bastard.
 190          */
 191         inode_unlock(dir);
 192         ret = get_target(symname, &path, &target_item, dentry->d_sb);
 193         inode_lock(dir);
 194         if (ret)
 195                 goto out_put;
 196 
 197         if (dentry->d_inode || d_unhashed(dentry))
 198                 ret = -EEXIST;
 199         else
 200                 ret = inode_permission(dir, MAY_WRITE | MAY_EXEC);
 201         if (!ret)
 202                 ret = type->ct_item_ops->allow_link(parent_item, target_item);
 203         if (!ret) {
 204                 mutex_lock(&configfs_symlink_mutex);
 205                 ret = create_link(parent_item, target_item, dentry);
 206                 mutex_unlock(&configfs_symlink_mutex);
 207                 if (ret && type->ct_item_ops->drop_link)
 208                         type->ct_item_ops->drop_link(parent_item,
 209                                                      target_item);
 210         }
 211 
 212         config_item_put(target_item);
 213         path_put(&path);
 214 
 215 out_put:
 216         config_item_put(parent_item);
 217         return ret;
 218 }
 219 
 220 int configfs_unlink(struct inode *dir, struct dentry *dentry)
 221 {
 222         struct configfs_dirent *sd = dentry->d_fsdata, *target_sd;
 223         struct config_item *parent_item;
 224         const struct config_item_type *type;
 225         int ret;
 226 
 227         ret = -EPERM;  /* What lack-of-symlink returns */
 228         if (!(sd->s_type & CONFIGFS_ITEM_LINK))
 229                 goto out;
 230 
 231         target_sd = sd->s_element;
 232 
 233         parent_item = configfs_get_config_item(dentry->d_parent);
 234         type = parent_item->ci_type;
 235 
 236         spin_lock(&configfs_dirent_lock);
 237         list_del_init(&sd->s_sibling);
 238         spin_unlock(&configfs_dirent_lock);
 239         configfs_drop_dentry(sd, dentry->d_parent);
 240         dput(dentry);
 241         configfs_put(sd);
 242 
 243         /*
 244          * drop_link() must be called before
 245          * decrementing target's ->s_links, so that the order of
 246          * drop_link(this, target) and drop_item(target) is preserved.
 247          */
 248         if (type && type->ct_item_ops &&
 249             type->ct_item_ops->drop_link)
 250                 type->ct_item_ops->drop_link(parent_item,
 251                                                target_sd->s_element);
 252 
 253         spin_lock(&configfs_dirent_lock);
 254         target_sd->s_links--;
 255         spin_unlock(&configfs_dirent_lock);
 256         configfs_put(target_sd);
 257 
 258         config_item_put(parent_item);
 259 
 260         ret = 0;
 261 
 262 out:
 263         return ret;
 264 }
 265 
 266 const struct inode_operations configfs_symlink_inode_operations = {
 267         .get_link = simple_get_link,
 268         .setattr = configfs_setattr,
 269 };
 270 

/* [<][>][^][v][top][bottom][index][help] */