166c7e1d3SDavid Howells /* dir.c: 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 4966c7e1d3SDavid Howells ret = dns_query("afsdb", name, len, "ipv4", 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); 8666c7e1d3SDavid Howells return 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 struct afs_vnode *vnode; 14566c7e1d3SDavid Howells struct inode *inode; 14666c7e1d3SDavid Howells int ret; 14766c7e1d3SDavid Howells 14866c7e1d3SDavid Howells vnode = AFS_FS_I(dir); 14966c7e1d3SDavid Howells 15066c7e1d3SDavid Howells _enter("%pd", dentry); 15166c7e1d3SDavid Howells 15266c7e1d3SDavid Howells ASSERTCMP(d_inode(dentry), ==, NULL); 15366c7e1d3SDavid Howells 15466c7e1d3SDavid Howells if (dentry->d_name.len >= AFSNAMEMAX) { 15566c7e1d3SDavid Howells _leave(" = -ENAMETOOLONG"); 15666c7e1d3SDavid Howells return ERR_PTR(-ENAMETOOLONG); 15766c7e1d3SDavid Howells } 15866c7e1d3SDavid Howells 15966c7e1d3SDavid Howells if (dentry->d_name.len == 5 && 16066c7e1d3SDavid Howells memcmp(dentry->d_name.name, "@cell", 5) == 0) 16166c7e1d3SDavid Howells return afs_lookup_atcell(dentry); 16266c7e1d3SDavid Howells 16366c7e1d3SDavid Howells inode = afs_try_auto_mntpt(dentry, dir); 16466c7e1d3SDavid Howells if (IS_ERR(inode)) { 16566c7e1d3SDavid Howells ret = PTR_ERR(inode); 16666c7e1d3SDavid Howells if (ret == -ENOENT) { 16766c7e1d3SDavid Howells d_add(dentry, NULL); 16866c7e1d3SDavid Howells _leave(" = NULL [negative]"); 16966c7e1d3SDavid Howells return NULL; 17066c7e1d3SDavid Howells } 17166c7e1d3SDavid Howells _leave(" = %d [do]", ret); 17266c7e1d3SDavid Howells return ERR_PTR(ret); 17366c7e1d3SDavid Howells } 17466c7e1d3SDavid Howells 17566c7e1d3SDavid Howells d_add(dentry, inode); 17666c7e1d3SDavid Howells _leave(" = 0 { ino=%lu v=%u }", 17766c7e1d3SDavid Howells d_inode(dentry)->i_ino, d_inode(dentry)->i_generation); 17866c7e1d3SDavid Howells return NULL; 17966c7e1d3SDavid Howells } 18066c7e1d3SDavid Howells 18166c7e1d3SDavid Howells const struct inode_operations afs_dynroot_inode_operations = { 18266c7e1d3SDavid Howells .lookup = afs_dynroot_lookup, 18366c7e1d3SDavid Howells }; 18466c7e1d3SDavid Howells 18566c7e1d3SDavid Howells /* 18666c7e1d3SDavid Howells * Dirs in the dynamic root don't need revalidation. 18766c7e1d3SDavid Howells */ 18866c7e1d3SDavid Howells static int afs_dynroot_d_revalidate(struct dentry *dentry, unsigned int flags) 18966c7e1d3SDavid Howells { 19066c7e1d3SDavid Howells return 1; 19166c7e1d3SDavid Howells } 19266c7e1d3SDavid Howells 19366c7e1d3SDavid Howells /* 19466c7e1d3SDavid Howells * Allow the VFS to enquire as to whether a dentry should be unhashed (mustn't 19566c7e1d3SDavid Howells * sleep) 19666c7e1d3SDavid Howells * - called from dput() when d_count is going to 0. 19766c7e1d3SDavid Howells * - return 1 to request dentry be unhashed, 0 otherwise 19866c7e1d3SDavid Howells */ 19966c7e1d3SDavid Howells static int afs_dynroot_d_delete(const struct dentry *dentry) 20066c7e1d3SDavid Howells { 20166c7e1d3SDavid Howells return d_really_is_positive(dentry); 20266c7e1d3SDavid Howells } 20366c7e1d3SDavid Howells 20466c7e1d3SDavid Howells const struct dentry_operations afs_dynroot_dentry_operations = { 20566c7e1d3SDavid Howells .d_revalidate = afs_dynroot_d_revalidate, 20666c7e1d3SDavid Howells .d_delete = afs_dynroot_d_delete, 20766c7e1d3SDavid Howells .d_release = afs_d_release, 20866c7e1d3SDavid Howells .d_automount = afs_d_automount, 20966c7e1d3SDavid Howells }; 210