10da0b7fdSDavid Howells /* AFS dynamic root handling 266c7e1d3SDavid Howells * 366c7e1d3SDavid Howells * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. 466c7e1d3SDavid Howells * Written by David Howells (dhowells@redhat.com) 566c7e1d3SDavid Howells * 666c7e1d3SDavid Howells * This program is free software; you can redistribute it and/or 766c7e1d3SDavid Howells * modify it under the terms of the GNU General Public Licence 866c7e1d3SDavid Howells * as published by the Free Software Foundation; either version 966c7e1d3SDavid Howells * 2 of the Licence, or (at your option) any later version. 1066c7e1d3SDavid Howells */ 1166c7e1d3SDavid Howells 1266c7e1d3SDavid Howells #include <linux/fs.h> 1366c7e1d3SDavid Howells #include <linux/namei.h> 1466c7e1d3SDavid Howells #include <linux/dns_resolver.h> 1566c7e1d3SDavid Howells #include "internal.h" 1666c7e1d3SDavid Howells 1766c7e1d3SDavid Howells const struct file_operations afs_dynroot_file_operations = { 1866c7e1d3SDavid Howells .open = dcache_dir_open, 1966c7e1d3SDavid Howells .release = dcache_dir_close, 2066c7e1d3SDavid Howells .iterate_shared = dcache_readdir, 2166c7e1d3SDavid Howells .llseek = dcache_dir_lseek, 2266c7e1d3SDavid Howells }; 2366c7e1d3SDavid Howells 2466c7e1d3SDavid Howells /* 2566c7e1d3SDavid Howells * Probe to see if a cell may exist. This prevents positive dentries from 2666c7e1d3SDavid Howells * being created unnecessarily. 2766c7e1d3SDavid Howells */ 2866c7e1d3SDavid Howells static int afs_probe_cell_name(struct dentry *dentry) 2966c7e1d3SDavid Howells { 3066c7e1d3SDavid Howells struct afs_cell *cell; 3166c7e1d3SDavid Howells const char *name = dentry->d_name.name; 3266c7e1d3SDavid Howells size_t len = dentry->d_name.len; 3366c7e1d3SDavid Howells int ret; 3466c7e1d3SDavid Howells 3566c7e1d3SDavid Howells /* Names prefixed with a dot are R/W mounts. */ 3666c7e1d3SDavid Howells if (name[0] == '.') { 3766c7e1d3SDavid Howells if (len == 1) 3866c7e1d3SDavid Howells return -EINVAL; 3966c7e1d3SDavid Howells name++; 4066c7e1d3SDavid Howells len--; 4166c7e1d3SDavid Howells } 4266c7e1d3SDavid Howells 4366c7e1d3SDavid Howells cell = afs_lookup_cell_rcu(afs_d2net(dentry), name, len); 4466c7e1d3SDavid Howells if (!IS_ERR(cell)) { 4566c7e1d3SDavid Howells afs_put_cell(afs_d2net(dentry), cell); 4666c7e1d3SDavid Howells return 0; 4766c7e1d3SDavid Howells } 4866c7e1d3SDavid Howells 49c88d5a7fSDavid Howells ret = dns_query("afsdb", name, len, "", NULL, NULL); 5066c7e1d3SDavid Howells if (ret == -ENODATA) 5166c7e1d3SDavid Howells ret = -EDESTADDRREQ; 5266c7e1d3SDavid Howells return ret; 5366c7e1d3SDavid Howells } 5466c7e1d3SDavid Howells 5566c7e1d3SDavid Howells /* 5666c7e1d3SDavid Howells * Try to auto mount the mountpoint with pseudo directory, if the autocell 5766c7e1d3SDavid Howells * operation is setted. 5866c7e1d3SDavid Howells */ 5966c7e1d3SDavid Howells struct inode *afs_try_auto_mntpt(struct dentry *dentry, struct inode *dir) 6066c7e1d3SDavid Howells { 6166c7e1d3SDavid Howells struct afs_vnode *vnode = AFS_FS_I(dir); 6266c7e1d3SDavid Howells struct inode *inode; 6366c7e1d3SDavid Howells int ret = -ENOENT; 6466c7e1d3SDavid Howells 6566c7e1d3SDavid Howells _enter("%p{%pd}, {%x:%u}", 6666c7e1d3SDavid Howells dentry, dentry, vnode->fid.vid, vnode->fid.vnode); 6766c7e1d3SDavid Howells 6866c7e1d3SDavid Howells if (!test_bit(AFS_VNODE_AUTOCELL, &vnode->flags)) 6966c7e1d3SDavid Howells goto out; 7066c7e1d3SDavid Howells 7166c7e1d3SDavid Howells ret = afs_probe_cell_name(dentry); 7266c7e1d3SDavid Howells if (ret < 0) 7366c7e1d3SDavid Howells goto out; 7466c7e1d3SDavid Howells 7566c7e1d3SDavid Howells inode = afs_iget_pseudo_dir(dir->i_sb, false); 7666c7e1d3SDavid Howells if (IS_ERR(inode)) { 7766c7e1d3SDavid Howells ret = PTR_ERR(inode); 7866c7e1d3SDavid Howells goto out; 7966c7e1d3SDavid Howells } 8066c7e1d3SDavid Howells 8166c7e1d3SDavid Howells _leave("= %p", inode); 8266c7e1d3SDavid Howells return inode; 8366c7e1d3SDavid Howells 8466c7e1d3SDavid Howells out: 8566c7e1d3SDavid Howells _leave("= %d", ret); 861401a0fcSAl Viro return ret == -ENOENT ? NULL : ERR_PTR(ret); 8766c7e1d3SDavid Howells } 8866c7e1d3SDavid Howells 8966c7e1d3SDavid Howells /* 9066c7e1d3SDavid Howells * Look up @cell in a dynroot directory. This is a substitution for the 9166c7e1d3SDavid Howells * local cell name for the net namespace. 9266c7e1d3SDavid Howells */ 9366c7e1d3SDavid Howells static struct dentry *afs_lookup_atcell(struct dentry *dentry) 9466c7e1d3SDavid Howells { 9566c7e1d3SDavid Howells struct afs_cell *cell; 9666c7e1d3SDavid Howells struct afs_net *net = afs_d2net(dentry); 9766c7e1d3SDavid Howells struct dentry *ret; 9866c7e1d3SDavid Howells unsigned int seq = 0; 9966c7e1d3SDavid Howells char *name; 10066c7e1d3SDavid Howells int len; 10166c7e1d3SDavid Howells 10266c7e1d3SDavid Howells if (!net->ws_cell) 10366c7e1d3SDavid Howells return ERR_PTR(-ENOENT); 10466c7e1d3SDavid Howells 10566c7e1d3SDavid Howells ret = ERR_PTR(-ENOMEM); 10666c7e1d3SDavid Howells name = kmalloc(AFS_MAXCELLNAME + 1, GFP_KERNEL); 10766c7e1d3SDavid Howells if (!name) 10866c7e1d3SDavid Howells goto out_p; 10966c7e1d3SDavid Howells 11066c7e1d3SDavid Howells rcu_read_lock(); 11166c7e1d3SDavid Howells do { 11266c7e1d3SDavid Howells read_seqbegin_or_lock(&net->cells_lock, &seq); 11366c7e1d3SDavid Howells cell = rcu_dereference_raw(net->ws_cell); 11466c7e1d3SDavid Howells if (cell) { 11566c7e1d3SDavid Howells len = cell->name_len; 11666c7e1d3SDavid Howells memcpy(name, cell->name, len + 1); 11766c7e1d3SDavid Howells } 11866c7e1d3SDavid Howells } while (need_seqretry(&net->cells_lock, seq)); 11966c7e1d3SDavid Howells done_seqretry(&net->cells_lock, seq); 12066c7e1d3SDavid Howells rcu_read_unlock(); 12166c7e1d3SDavid Howells 12266c7e1d3SDavid Howells ret = ERR_PTR(-ENOENT); 12366c7e1d3SDavid Howells if (!cell) 12466c7e1d3SDavid Howells goto out_n; 12566c7e1d3SDavid Howells 12666c7e1d3SDavid Howells ret = lookup_one_len(name, dentry->d_parent, len); 12766c7e1d3SDavid Howells 12866c7e1d3SDavid Howells /* We don't want to d_add() the @cell dentry here as we don't want to 12966c7e1d3SDavid Howells * the cached dentry to hide changes to the local cell name. 13066c7e1d3SDavid Howells */ 13166c7e1d3SDavid Howells 13266c7e1d3SDavid Howells out_n: 13366c7e1d3SDavid Howells kfree(name); 13466c7e1d3SDavid Howells out_p: 13566c7e1d3SDavid Howells return ret; 13666c7e1d3SDavid Howells } 13766c7e1d3SDavid Howells 13866c7e1d3SDavid Howells /* 13966c7e1d3SDavid Howells * Look up an entry in a dynroot directory. 14066c7e1d3SDavid Howells */ 14166c7e1d3SDavid Howells static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry, 14266c7e1d3SDavid Howells unsigned int flags) 14366c7e1d3SDavid Howells { 14466c7e1d3SDavid Howells _enter("%pd", dentry); 14566c7e1d3SDavid Howells 14666c7e1d3SDavid Howells ASSERTCMP(d_inode(dentry), ==, NULL); 14766c7e1d3SDavid Howells 14866c7e1d3SDavid Howells if (dentry->d_name.len >= AFSNAMEMAX) { 14966c7e1d3SDavid Howells _leave(" = -ENAMETOOLONG"); 15066c7e1d3SDavid Howells return ERR_PTR(-ENAMETOOLONG); 15166c7e1d3SDavid Howells } 15266c7e1d3SDavid Howells 15366c7e1d3SDavid Howells if (dentry->d_name.len == 5 && 15466c7e1d3SDavid Howells memcmp(dentry->d_name.name, "@cell", 5) == 0) 15566c7e1d3SDavid Howells return afs_lookup_atcell(dentry); 15666c7e1d3SDavid Howells 1571401a0fcSAl Viro return d_splice_alias(afs_try_auto_mntpt(dentry, dir), dentry); 15866c7e1d3SDavid Howells } 15966c7e1d3SDavid Howells 16066c7e1d3SDavid Howells const struct inode_operations afs_dynroot_inode_operations = { 16166c7e1d3SDavid Howells .lookup = afs_dynroot_lookup, 16266c7e1d3SDavid Howells }; 16366c7e1d3SDavid Howells 16466c7e1d3SDavid Howells /* 16566c7e1d3SDavid Howells * Dirs in the dynamic root don't need revalidation. 16666c7e1d3SDavid Howells */ 16766c7e1d3SDavid Howells static int afs_dynroot_d_revalidate(struct dentry *dentry, unsigned int flags) 16866c7e1d3SDavid Howells { 16966c7e1d3SDavid Howells return 1; 17066c7e1d3SDavid Howells } 17166c7e1d3SDavid Howells 17266c7e1d3SDavid Howells /* 17366c7e1d3SDavid Howells * Allow the VFS to enquire as to whether a dentry should be unhashed (mustn't 17466c7e1d3SDavid Howells * sleep) 17566c7e1d3SDavid Howells * - called from dput() when d_count is going to 0. 17666c7e1d3SDavid Howells * - return 1 to request dentry be unhashed, 0 otherwise 17766c7e1d3SDavid Howells */ 17866c7e1d3SDavid Howells static int afs_dynroot_d_delete(const struct dentry *dentry) 17966c7e1d3SDavid Howells { 18066c7e1d3SDavid Howells return d_really_is_positive(dentry); 18166c7e1d3SDavid Howells } 18266c7e1d3SDavid Howells 18366c7e1d3SDavid Howells const struct dentry_operations afs_dynroot_dentry_operations = { 18466c7e1d3SDavid Howells .d_revalidate = afs_dynroot_d_revalidate, 18566c7e1d3SDavid Howells .d_delete = afs_dynroot_d_delete, 18666c7e1d3SDavid Howells .d_release = afs_d_release, 18766c7e1d3SDavid Howells .d_automount = afs_d_automount, 18866c7e1d3SDavid Howells }; 1890da0b7fdSDavid Howells 1900da0b7fdSDavid Howells /* 1910da0b7fdSDavid Howells * Create a manually added cell mount directory. 1920da0b7fdSDavid Howells * - The caller must hold net->proc_cells_lock 1930da0b7fdSDavid Howells */ 1940da0b7fdSDavid Howells int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell) 1950da0b7fdSDavid Howells { 1960da0b7fdSDavid Howells struct super_block *sb = net->dynroot_sb; 1970da0b7fdSDavid Howells struct dentry *root, *subdir; 1980da0b7fdSDavid Howells int ret; 1990da0b7fdSDavid Howells 2000da0b7fdSDavid Howells if (!sb || atomic_read(&sb->s_active) == 0) 2010da0b7fdSDavid Howells return 0; 2020da0b7fdSDavid Howells 2030da0b7fdSDavid Howells /* Let the ->lookup op do the creation */ 2040da0b7fdSDavid Howells root = sb->s_root; 2050da0b7fdSDavid Howells inode_lock(root->d_inode); 2060da0b7fdSDavid Howells subdir = lookup_one_len(cell->name, root, cell->name_len); 2070da0b7fdSDavid Howells if (IS_ERR(subdir)) { 2080da0b7fdSDavid Howells ret = PTR_ERR(subdir); 2090da0b7fdSDavid Howells goto unlock; 2100da0b7fdSDavid Howells } 2110da0b7fdSDavid Howells 2120da0b7fdSDavid Howells /* Note that we're retaining an extra ref on the dentry */ 2130da0b7fdSDavid Howells subdir->d_fsdata = (void *)1UL; 2140da0b7fdSDavid Howells ret = 0; 2150da0b7fdSDavid Howells unlock: 2160da0b7fdSDavid Howells inode_unlock(root->d_inode); 2170da0b7fdSDavid Howells return ret; 2180da0b7fdSDavid Howells } 2190da0b7fdSDavid Howells 2200da0b7fdSDavid Howells /* 2210da0b7fdSDavid Howells * Remove a manually added cell mount directory. 2220da0b7fdSDavid Howells * - The caller must hold net->proc_cells_lock 2230da0b7fdSDavid Howells */ 2240da0b7fdSDavid Howells void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell) 2250da0b7fdSDavid Howells { 2260da0b7fdSDavid Howells struct super_block *sb = net->dynroot_sb; 2270da0b7fdSDavid Howells struct dentry *root, *subdir; 2280da0b7fdSDavid Howells 2290da0b7fdSDavid Howells if (!sb || atomic_read(&sb->s_active) == 0) 2300da0b7fdSDavid Howells return; 2310da0b7fdSDavid Howells 2320da0b7fdSDavid Howells root = sb->s_root; 2330da0b7fdSDavid Howells inode_lock(root->d_inode); 2340da0b7fdSDavid Howells 2350da0b7fdSDavid Howells /* Don't want to trigger a lookup call, which will re-add the cell */ 2360da0b7fdSDavid Howells subdir = try_lookup_one_len(cell->name, root, cell->name_len); 2370da0b7fdSDavid Howells if (IS_ERR_OR_NULL(subdir)) { 2380da0b7fdSDavid Howells _debug("lookup %ld", PTR_ERR(subdir)); 2390da0b7fdSDavid Howells goto no_dentry; 2400da0b7fdSDavid Howells } 2410da0b7fdSDavid Howells 2420da0b7fdSDavid Howells _debug("rmdir %pd %u", subdir, d_count(subdir)); 2430da0b7fdSDavid Howells 2440da0b7fdSDavid Howells if (subdir->d_fsdata) { 2450da0b7fdSDavid Howells _debug("unpin %u", d_count(subdir)); 2460da0b7fdSDavid Howells subdir->d_fsdata = NULL; 2470da0b7fdSDavid Howells dput(subdir); 2480da0b7fdSDavid Howells } 2490da0b7fdSDavid Howells dput(subdir); 2500da0b7fdSDavid Howells no_dentry: 2510da0b7fdSDavid Howells inode_unlock(root->d_inode); 2520da0b7fdSDavid Howells _leave(""); 2530da0b7fdSDavid Howells } 2540da0b7fdSDavid Howells 2550da0b7fdSDavid Howells /* 2560da0b7fdSDavid Howells * Populate a newly created dynamic root with cell names. 2570da0b7fdSDavid Howells */ 2580da0b7fdSDavid Howells int afs_dynroot_populate(struct super_block *sb) 2590da0b7fdSDavid Howells { 2600da0b7fdSDavid Howells struct afs_cell *cell; 2610da0b7fdSDavid Howells struct afs_net *net = afs_sb2net(sb); 2620da0b7fdSDavid Howells int ret; 2630da0b7fdSDavid Howells 2640da0b7fdSDavid Howells if (mutex_lock_interruptible(&net->proc_cells_lock) < 0) 2650da0b7fdSDavid Howells return -ERESTARTSYS; 2660da0b7fdSDavid Howells 2670da0b7fdSDavid Howells net->dynroot_sb = sb; 2680da0b7fdSDavid Howells list_for_each_entry(cell, &net->proc_cells, proc_link) { 2690da0b7fdSDavid Howells ret = afs_dynroot_mkdir(net, cell); 2700da0b7fdSDavid Howells if (ret < 0) 2710da0b7fdSDavid Howells goto error; 2720da0b7fdSDavid Howells } 2730da0b7fdSDavid Howells 2740da0b7fdSDavid Howells ret = 0; 2750da0b7fdSDavid Howells out: 2760da0b7fdSDavid Howells mutex_unlock(&net->proc_cells_lock); 2770da0b7fdSDavid Howells return ret; 2780da0b7fdSDavid Howells 2790da0b7fdSDavid Howells error: 2800da0b7fdSDavid Howells net->dynroot_sb = NULL; 2810da0b7fdSDavid Howells goto out; 2820da0b7fdSDavid Howells } 2830da0b7fdSDavid Howells 2840da0b7fdSDavid Howells /* 2850da0b7fdSDavid Howells * When a dynamic root that's in the process of being destroyed, depopulate it 2860da0b7fdSDavid Howells * of pinned directories. 2870da0b7fdSDavid Howells */ 2880da0b7fdSDavid Howells void afs_dynroot_depopulate(struct super_block *sb) 2890da0b7fdSDavid Howells { 2900da0b7fdSDavid Howells struct afs_net *net = afs_sb2net(sb); 2910da0b7fdSDavid Howells struct dentry *root = sb->s_root, *subdir, *tmp; 2920da0b7fdSDavid Howells 2930da0b7fdSDavid Howells /* Prevent more subdirs from being created */ 2940da0b7fdSDavid Howells mutex_lock(&net->proc_cells_lock); 2950da0b7fdSDavid Howells if (net->dynroot_sb == sb) 2960da0b7fdSDavid Howells net->dynroot_sb = NULL; 2970da0b7fdSDavid Howells mutex_unlock(&net->proc_cells_lock); 2980da0b7fdSDavid Howells 2990da0b7fdSDavid Howells inode_lock(root->d_inode); 3000da0b7fdSDavid Howells 3010da0b7fdSDavid Howells /* Remove all the pins for dirs created for manually added cells */ 3020da0b7fdSDavid Howells list_for_each_entry_safe(subdir, tmp, &root->d_subdirs, d_child) { 3030da0b7fdSDavid Howells if (subdir->d_fsdata) { 3040da0b7fdSDavid Howells subdir->d_fsdata = NULL; 3050da0b7fdSDavid Howells dput(subdir); 3060da0b7fdSDavid Howells } 3070da0b7fdSDavid Howells } 3080da0b7fdSDavid Howells 3090da0b7fdSDavid Howells inode_unlock(root->d_inode); 3100da0b7fdSDavid Howells } 311