| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* -*- mode: c; c-basic-offset: 8; -*- |
|---|
| 2 | 3 | * vim: noexpandtab sw=8 ts=8 sts=0: |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * dir.c - Operations for configfs directories. |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or |
|---|
| 7 | | - * modify it under the terms of the GNU General Public |
|---|
| 8 | | - * License as published by the Free Software Foundation; either |
|---|
| 9 | | - * version 2 of the License, or (at your option) any later version. |
|---|
| 10 | | - * |
|---|
| 11 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 12 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 13 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 14 | | - * General Public License for more details. |
|---|
| 15 | | - * |
|---|
| 16 | | - * You should have received a copy of the GNU General Public |
|---|
| 17 | | - * License along with this program; if not, write to the |
|---|
| 18 | | - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
|---|
| 19 | | - * Boston, MA 021110-1307, USA. |
|---|
| 20 | 6 | * |
|---|
| 21 | 7 | * Based on sysfs: |
|---|
| 22 | 8 | * sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel |
|---|
| .. | .. |
|---|
| 27 | 13 | #undef DEBUG |
|---|
| 28 | 14 | |
|---|
| 29 | 15 | #include <linux/fs.h> |
|---|
| 16 | +#include <linux/fsnotify.h> |
|---|
| 30 | 17 | #include <linux/mount.h> |
|---|
| 31 | 18 | #include <linux/module.h> |
|---|
| 32 | 19 | #include <linux/slab.h> |
|---|
| .. | .. |
|---|
| 35 | 22 | #include <linux/configfs.h> |
|---|
| 36 | 23 | #include "configfs_internal.h" |
|---|
| 37 | 24 | |
|---|
| 38 | | -DECLARE_RWSEM(configfs_rename_sem); |
|---|
| 39 | 25 | /* |
|---|
| 40 | 26 | * Protects mutations of configfs_dirent linkage together with proper i_mutex |
|---|
| 41 | 27 | * Also protects mutations of symlinks linkage to target configfs_dirent |
|---|
| .. | .. |
|---|
| 212 | 198 | return ERR_PTR(-ENOMEM); |
|---|
| 213 | 199 | |
|---|
| 214 | 200 | atomic_set(&sd->s_count, 1); |
|---|
| 215 | | - INIT_LIST_HEAD(&sd->s_links); |
|---|
| 216 | 201 | INIT_LIST_HEAD(&sd->s_children); |
|---|
| 217 | 202 | sd->s_element = element; |
|---|
| 218 | 203 | sd->s_type = type; |
|---|
| .. | .. |
|---|
| 274 | 259 | return 0; |
|---|
| 275 | 260 | } |
|---|
| 276 | 261 | |
|---|
| 277 | | -static void init_dir(struct inode * inode) |
|---|
| 262 | +static void configfs_remove_dirent(struct dentry *dentry) |
|---|
| 278 | 263 | { |
|---|
| 279 | | - inode->i_op = &configfs_dir_inode_operations; |
|---|
| 280 | | - inode->i_fop = &configfs_dir_operations; |
|---|
| 264 | + struct configfs_dirent *sd = dentry->d_fsdata; |
|---|
| 281 | 265 | |
|---|
| 282 | | - /* directory inodes start off with i_nlink == 2 (for "." entry) */ |
|---|
| 283 | | - inc_nlink(inode); |
|---|
| 284 | | -} |
|---|
| 285 | | - |
|---|
| 286 | | -static void configfs_init_file(struct inode * inode) |
|---|
| 287 | | -{ |
|---|
| 288 | | - inode->i_size = PAGE_SIZE; |
|---|
| 289 | | - inode->i_fop = &configfs_file_operations; |
|---|
| 290 | | -} |
|---|
| 291 | | - |
|---|
| 292 | | -static void configfs_init_bin_file(struct inode *inode) |
|---|
| 293 | | -{ |
|---|
| 294 | | - inode->i_size = 0; |
|---|
| 295 | | - inode->i_fop = &configfs_bin_file_operations; |
|---|
| 296 | | -} |
|---|
| 297 | | - |
|---|
| 298 | | -static void init_symlink(struct inode * inode) |
|---|
| 299 | | -{ |
|---|
| 300 | | - inode->i_op = &configfs_symlink_inode_operations; |
|---|
| 266 | + if (!sd) |
|---|
| 267 | + return; |
|---|
| 268 | + spin_lock(&configfs_dirent_lock); |
|---|
| 269 | + list_del_init(&sd->s_sibling); |
|---|
| 270 | + spin_unlock(&configfs_dirent_lock); |
|---|
| 271 | + configfs_put(sd); |
|---|
| 301 | 272 | } |
|---|
| 302 | 273 | |
|---|
| 303 | 274 | /** |
|---|
| .. | .. |
|---|
| 315 | 286 | int error; |
|---|
| 316 | 287 | umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; |
|---|
| 317 | 288 | struct dentry *p = dentry->d_parent; |
|---|
| 289 | + struct inode *inode; |
|---|
| 318 | 290 | |
|---|
| 319 | 291 | BUG_ON(!item); |
|---|
| 320 | 292 | |
|---|
| .. | .. |
|---|
| 329 | 301 | return error; |
|---|
| 330 | 302 | |
|---|
| 331 | 303 | configfs_set_dir_dirent_depth(p->d_fsdata, dentry->d_fsdata); |
|---|
| 332 | | - error = configfs_create(dentry, mode, init_dir); |
|---|
| 333 | | - if (!error) { |
|---|
| 334 | | - inc_nlink(d_inode(p)); |
|---|
| 335 | | - item->ci_dentry = dentry; |
|---|
| 336 | | - } else { |
|---|
| 337 | | - struct configfs_dirent *sd = dentry->d_fsdata; |
|---|
| 338 | | - if (sd) { |
|---|
| 339 | | - spin_lock(&configfs_dirent_lock); |
|---|
| 340 | | - list_del_init(&sd->s_sibling); |
|---|
| 341 | | - spin_unlock(&configfs_dirent_lock); |
|---|
| 342 | | - configfs_put(sd); |
|---|
| 343 | | - } |
|---|
| 344 | | - } |
|---|
| 345 | | - return error; |
|---|
| 304 | + inode = configfs_create(dentry, mode); |
|---|
| 305 | + if (IS_ERR(inode)) |
|---|
| 306 | + goto out_remove; |
|---|
| 307 | + |
|---|
| 308 | + inode->i_op = &configfs_dir_inode_operations; |
|---|
| 309 | + inode->i_fop = &configfs_dir_operations; |
|---|
| 310 | + /* directory inodes start off with i_nlink == 2 (for "." entry) */ |
|---|
| 311 | + inc_nlink(inode); |
|---|
| 312 | + d_instantiate(dentry, inode); |
|---|
| 313 | + /* already hashed */ |
|---|
| 314 | + dget(dentry); /* pin directory dentries in core */ |
|---|
| 315 | + inc_nlink(d_inode(p)); |
|---|
| 316 | + item->ci_dentry = dentry; |
|---|
| 317 | + return 0; |
|---|
| 318 | + |
|---|
| 319 | +out_remove: |
|---|
| 320 | + configfs_remove_dirent(dentry); |
|---|
| 321 | + return PTR_ERR(inode); |
|---|
| 346 | 322 | } |
|---|
| 347 | 323 | |
|---|
| 348 | 324 | /* |
|---|
| .. | .. |
|---|
| 383 | 359 | return ret; |
|---|
| 384 | 360 | } |
|---|
| 385 | 361 | |
|---|
| 386 | | -int configfs_create_link(struct configfs_symlink *sl, |
|---|
| 387 | | - struct dentry *parent, |
|---|
| 388 | | - struct dentry *dentry) |
|---|
| 362 | +int configfs_create_link(struct configfs_dirent *target, struct dentry *parent, |
|---|
| 363 | + struct dentry *dentry, char *body) |
|---|
| 389 | 364 | { |
|---|
| 390 | 365 | int err = 0; |
|---|
| 391 | 366 | umode_t mode = S_IFLNK | S_IRWXUGO; |
|---|
| 392 | 367 | struct configfs_dirent *p = parent->d_fsdata; |
|---|
| 368 | + struct inode *inode; |
|---|
| 393 | 369 | |
|---|
| 394 | | - err = configfs_make_dirent(p, dentry, sl, mode, |
|---|
| 395 | | - CONFIGFS_ITEM_LINK, p->s_frag); |
|---|
| 396 | | - if (!err) { |
|---|
| 397 | | - err = configfs_create(dentry, mode, init_symlink); |
|---|
| 398 | | - if (err) { |
|---|
| 399 | | - struct configfs_dirent *sd = dentry->d_fsdata; |
|---|
| 400 | | - if (sd) { |
|---|
| 401 | | - spin_lock(&configfs_dirent_lock); |
|---|
| 402 | | - list_del_init(&sd->s_sibling); |
|---|
| 403 | | - spin_unlock(&configfs_dirent_lock); |
|---|
| 404 | | - configfs_put(sd); |
|---|
| 405 | | - } |
|---|
| 406 | | - } |
|---|
| 407 | | - } |
|---|
| 408 | | - return err; |
|---|
| 370 | + err = configfs_make_dirent(p, dentry, target, mode, CONFIGFS_ITEM_LINK, |
|---|
| 371 | + p->s_frag); |
|---|
| 372 | + if (err) |
|---|
| 373 | + return err; |
|---|
| 374 | + |
|---|
| 375 | + inode = configfs_create(dentry, mode); |
|---|
| 376 | + if (IS_ERR(inode)) |
|---|
| 377 | + goto out_remove; |
|---|
| 378 | + |
|---|
| 379 | + inode->i_link = body; |
|---|
| 380 | + inode->i_op = &configfs_symlink_inode_operations; |
|---|
| 381 | + d_instantiate(dentry, inode); |
|---|
| 382 | + dget(dentry); /* pin link dentries in core */ |
|---|
| 383 | + return 0; |
|---|
| 384 | + |
|---|
| 385 | +out_remove: |
|---|
| 386 | + configfs_remove_dirent(dentry); |
|---|
| 387 | + return PTR_ERR(inode); |
|---|
| 409 | 388 | } |
|---|
| 410 | 389 | |
|---|
| 411 | 390 | static void remove_dir(struct dentry * d) |
|---|
| 412 | 391 | { |
|---|
| 413 | 392 | struct dentry * parent = dget(d->d_parent); |
|---|
| 414 | | - struct configfs_dirent * sd; |
|---|
| 415 | 393 | |
|---|
| 416 | | - sd = d->d_fsdata; |
|---|
| 417 | | - spin_lock(&configfs_dirent_lock); |
|---|
| 418 | | - list_del_init(&sd->s_sibling); |
|---|
| 419 | | - spin_unlock(&configfs_dirent_lock); |
|---|
| 420 | | - configfs_put(sd); |
|---|
| 394 | + configfs_remove_dirent(d); |
|---|
| 395 | + |
|---|
| 421 | 396 | if (d_really_is_positive(d)) |
|---|
| 422 | 397 | simple_rmdir(d_inode(parent),d); |
|---|
| 423 | 398 | |
|---|
| .. | .. |
|---|
| 458 | 433 | static int configfs_attach_attr(struct configfs_dirent * sd, struct dentry * dentry) |
|---|
| 459 | 434 | { |
|---|
| 460 | 435 | struct configfs_attribute * attr = sd->s_element; |
|---|
| 461 | | - int error; |
|---|
| 436 | + struct inode *inode; |
|---|
| 462 | 437 | |
|---|
| 463 | 438 | spin_lock(&configfs_dirent_lock); |
|---|
| 464 | 439 | dentry->d_fsdata = configfs_get(sd); |
|---|
| 465 | 440 | sd->s_dentry = dentry; |
|---|
| 466 | 441 | spin_unlock(&configfs_dirent_lock); |
|---|
| 467 | 442 | |
|---|
| 468 | | - error = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG, |
|---|
| 469 | | - (sd->s_type & CONFIGFS_ITEM_BIN_ATTR) ? |
|---|
| 470 | | - configfs_init_bin_file : |
|---|
| 471 | | - configfs_init_file); |
|---|
| 472 | | - if (error) |
|---|
| 443 | + inode = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG); |
|---|
| 444 | + if (IS_ERR(inode)) { |
|---|
| 473 | 445 | configfs_put(sd); |
|---|
| 474 | | - return error; |
|---|
| 446 | + return PTR_ERR(inode); |
|---|
| 447 | + } |
|---|
| 448 | + if (sd->s_type & CONFIGFS_ITEM_BIN_ATTR) { |
|---|
| 449 | + inode->i_size = 0; |
|---|
| 450 | + inode->i_fop = &configfs_bin_file_operations; |
|---|
| 451 | + } else { |
|---|
| 452 | + inode->i_size = PAGE_SIZE; |
|---|
| 453 | + inode->i_fop = &configfs_file_operations; |
|---|
| 454 | + } |
|---|
| 455 | + d_add(dentry, inode); |
|---|
| 456 | + return 0; |
|---|
| 475 | 457 | } |
|---|
| 476 | 458 | |
|---|
| 477 | 459 | static struct dentry * configfs_lookup(struct inode *dir, |
|---|
| .. | .. |
|---|
| 541 | 523 | parent_sd->s_type |= CONFIGFS_USET_DROPPING; |
|---|
| 542 | 524 | |
|---|
| 543 | 525 | ret = -EBUSY; |
|---|
| 544 | | - if (!list_empty(&parent_sd->s_links)) |
|---|
| 526 | + if (parent_sd->s_links) |
|---|
| 545 | 527 | goto out; |
|---|
| 546 | 528 | |
|---|
| 547 | 529 | ret = 0; |
|---|
| .. | .. |
|---|
| 1194 | 1176 | |
|---|
| 1195 | 1177 | /* |
|---|
| 1196 | 1178 | * Release the dependent linkage. This is much simpler than |
|---|
| 1197 | | - * configfs_depend_item() because we know that that the client driver is |
|---|
| 1179 | + * configfs_depend_item() because we know that the client driver is |
|---|
| 1198 | 1180 | * pinned, thus the subsystem is pinned, and therefore configfs is pinned. |
|---|
| 1199 | 1181 | */ |
|---|
| 1200 | 1182 | void configfs_undepend_item(struct config_item *target) |
|---|
| .. | .. |
|---|
| 1435 | 1417 | else |
|---|
| 1436 | 1418 | ret = configfs_attach_item(parent_item, item, dentry, frag); |
|---|
| 1437 | 1419 | |
|---|
| 1420 | + /* inherit uid/gid from process creating the directory */ |
|---|
| 1421 | + if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID) || |
|---|
| 1422 | + !gid_eq(current_fsgid(), GLOBAL_ROOT_GID)) { |
|---|
| 1423 | + struct iattr ia = { |
|---|
| 1424 | + .ia_uid = current_fsuid(), |
|---|
| 1425 | + .ia_gid = current_fsgid(), |
|---|
| 1426 | + .ia_valid = ATTR_UID | ATTR_GID, |
|---|
| 1427 | + }; |
|---|
| 1428 | + struct inode *inode = d_inode(dentry); |
|---|
| 1429 | + inode->i_uid = ia.ia_uid; |
|---|
| 1430 | + inode->i_gid = ia.ia_gid; |
|---|
| 1431 | + /* the above manual assignments skip the permission checks */ |
|---|
| 1432 | + configfs_setattr(dentry, &ia); |
|---|
| 1433 | + } |
|---|
| 1434 | + |
|---|
| 1438 | 1435 | spin_lock(&configfs_dirent_lock); |
|---|
| 1439 | 1436 | sd->s_type &= ~CONFIGFS_USET_IN_MKDIR; |
|---|
| 1440 | 1437 | if (!ret) |
|---|
| .. | .. |
|---|
| 1600 | 1597 | .setattr = configfs_setattr, |
|---|
| 1601 | 1598 | }; |
|---|
| 1602 | 1599 | |
|---|
| 1603 | | -#if 0 |
|---|
| 1604 | | -int configfs_rename_dir(struct config_item * item, const char *new_name) |
|---|
| 1605 | | -{ |
|---|
| 1606 | | - int error = 0; |
|---|
| 1607 | | - struct dentry * new_dentry, * parent; |
|---|
| 1608 | | - |
|---|
| 1609 | | - if (!strcmp(config_item_name(item), new_name)) |
|---|
| 1610 | | - return -EINVAL; |
|---|
| 1611 | | - |
|---|
| 1612 | | - if (!item->parent) |
|---|
| 1613 | | - return -EINVAL; |
|---|
| 1614 | | - |
|---|
| 1615 | | - down_write(&configfs_rename_sem); |
|---|
| 1616 | | - parent = item->parent->dentry; |
|---|
| 1617 | | - |
|---|
| 1618 | | - inode_lock(d_inode(parent)); |
|---|
| 1619 | | - |
|---|
| 1620 | | - new_dentry = lookup_one_len(new_name, parent, strlen(new_name)); |
|---|
| 1621 | | - if (!IS_ERR(new_dentry)) { |
|---|
| 1622 | | - if (d_really_is_negative(new_dentry)) { |
|---|
| 1623 | | - error = config_item_set_name(item, "%s", new_name); |
|---|
| 1624 | | - if (!error) { |
|---|
| 1625 | | - d_add(new_dentry, NULL); |
|---|
| 1626 | | - d_move(item->dentry, new_dentry); |
|---|
| 1627 | | - } |
|---|
| 1628 | | - else |
|---|
| 1629 | | - d_delete(new_dentry); |
|---|
| 1630 | | - } else |
|---|
| 1631 | | - error = -EEXIST; |
|---|
| 1632 | | - dput(new_dentry); |
|---|
| 1633 | | - } |
|---|
| 1634 | | - inode_unlock(d_inode(parent)); |
|---|
| 1635 | | - up_write(&configfs_rename_sem); |
|---|
| 1636 | | - |
|---|
| 1637 | | - return error; |
|---|
| 1638 | | -} |
|---|
| 1639 | | -#endif |
|---|
| 1640 | | - |
|---|
| 1641 | 1600 | static int configfs_dir_open(struct inode *inode, struct file *file) |
|---|
| 1642 | 1601 | { |
|---|
| 1643 | 1602 | struct dentry * dentry = file->f_path.dentry; |
|---|
| .. | .. |
|---|
| 1752 | 1711 | switch (whence) { |
|---|
| 1753 | 1712 | case 1: |
|---|
| 1754 | 1713 | offset += file->f_pos; |
|---|
| 1714 | + fallthrough; |
|---|
| 1755 | 1715 | case 0: |
|---|
| 1756 | 1716 | if (offset >= 0) |
|---|
| 1757 | 1717 | break; |
|---|
| 1718 | + fallthrough; |
|---|
| 1758 | 1719 | default: |
|---|
| 1759 | 1720 | return -EINVAL; |
|---|
| 1760 | 1721 | } |
|---|
| .. | .. |
|---|
| 1867 | 1828 | configfs_detach_group(&group->cg_item); |
|---|
| 1868 | 1829 | d_inode(dentry)->i_flags |= S_DEAD; |
|---|
| 1869 | 1830 | dont_mount(dentry); |
|---|
| 1870 | | - d_delete(dentry); |
|---|
| 1831 | + d_drop(dentry); |
|---|
| 1832 | + fsnotify_rmdir(d_inode(parent), dentry); |
|---|
| 1871 | 1833 | inode_unlock(d_inode(parent)); |
|---|
| 1872 | 1834 | |
|---|
| 1873 | 1835 | dput(dentry); |
|---|
| .. | .. |
|---|
| 2014 | 1976 | dont_mount(dentry); |
|---|
| 2015 | 1977 | inode_unlock(d_inode(dentry)); |
|---|
| 2016 | 1978 | |
|---|
| 2017 | | - d_delete(dentry); |
|---|
| 1979 | + d_drop(dentry); |
|---|
| 1980 | + fsnotify_rmdir(d_inode(root), dentry); |
|---|
| 2018 | 1981 | |
|---|
| 2019 | 1982 | inode_unlock(d_inode(root)); |
|---|
| 2020 | 1983 | |
|---|