1 /* mountpoint management 2 * 3 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. 4 * Written by David Howells (dhowells@redhat.com) 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 9 * 2 of the License, or (at your option) any later version. 10 */ 11 12 #include <linux/kernel.h> 13 #include <linux/module.h> 14 #include <linux/init.h> 15 #include <linux/slab.h> 16 #include <linux/fs.h> 17 #include <linux/pagemap.h> 18 #include <linux/mount.h> 19 #include <linux/namei.h> 20 #include <linux/mnt_namespace.h> 21 #include "internal.h" 22 23 24 static struct dentry *afs_mntpt_lookup(struct inode *dir, 25 struct dentry *dentry, 26 struct nameidata *nd); 27 static int afs_mntpt_open(struct inode *inode, struct file *file); 28 static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd); 29 static void afs_mntpt_expiry_timed_out(struct work_struct *work); 30 31 const struct file_operations afs_mntpt_file_operations = { 32 .open = afs_mntpt_open, 33 }; 34 35 const struct inode_operations afs_mntpt_inode_operations = { 36 .lookup = afs_mntpt_lookup, 37 .follow_link = afs_mntpt_follow_link, 38 .readlink = page_readlink, 39 .getattr = afs_getattr, 40 }; 41 42 static LIST_HEAD(afs_vfsmounts); 43 static DECLARE_DELAYED_WORK(afs_mntpt_expiry_timer, afs_mntpt_expiry_timed_out); 44 45 static unsigned long afs_mntpt_expiry_timeout = 10 * 60; 46 47 /* 48 * check a symbolic link to see whether it actually encodes a mountpoint 49 * - sets the AFS_VNODE_MOUNTPOINT flag on the vnode appropriately 50 */ 51 int afs_mntpt_check_symlink(struct afs_vnode *vnode, struct key *key) 52 { 53 struct file file = { 54 .private_data = key, 55 }; 56 struct page *page; 57 size_t size; 58 char *buf; 59 int ret; 60 61 _enter("{%x:%u,%u}", 62 vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique); 63 64 /* read the contents of the symlink into the pagecache */ 65 page = read_mapping_page(AFS_VNODE_TO_I(vnode)->i_mapping, 0, &file); 66 if (IS_ERR(page)) { 67 ret = PTR_ERR(page); 68 goto out; 69 } 70 71 ret = -EIO; 72 if (PageError(page)) 73 goto out_free; 74 75 buf = kmap(page); 76 77 /* examine the symlink's contents */ 78 size = vnode->status.size; 79 _debug("symlink to %*.*s", (int) size, (int) size, buf); 80 81 if (size > 2 && 82 (buf[0] == '%' || buf[0] == '#') && 83 buf[size - 1] == '.' 84 ) { 85 _debug("symlink is a mountpoint"); 86 spin_lock(&vnode->lock); 87 set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags); 88 spin_unlock(&vnode->lock); 89 } 90 91 ret = 0; 92 93 kunmap(page); 94 out_free: 95 page_cache_release(page); 96 out: 97 _leave(" = %d", ret); 98 return ret; 99 } 100 101 /* 102 * no valid lookup procedure on this sort of dir 103 */ 104 static struct dentry *afs_mntpt_lookup(struct inode *dir, 105 struct dentry *dentry, 106 struct nameidata *nd) 107 { 108 _enter("%p,%p{%p{%s},%s}", 109 dir, 110 dentry, 111 dentry->d_parent, 112 dentry->d_parent ? 113 dentry->d_parent->d_name.name : (const unsigned char *) "", 114 dentry->d_name.name); 115 116 return ERR_PTR(-EREMOTE); 117 } 118 119 /* 120 * no valid open procedure on this sort of dir 121 */ 122 static int afs_mntpt_open(struct inode *inode, struct file *file) 123 { 124 _enter("%p,%p{%p{%s},%s}", 125 inode, file, 126 file->f_path.dentry->d_parent, 127 file->f_path.dentry->d_parent ? 128 file->f_path.dentry->d_parent->d_name.name : 129 (const unsigned char *) "", 130 file->f_path.dentry->d_name.name); 131 132 return -EREMOTE; 133 } 134 135 /* 136 * create a vfsmount to be automounted 137 */ 138 static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) 139 { 140 struct afs_super_info *super; 141 struct vfsmount *mnt; 142 struct page *page = NULL; 143 size_t size; 144 char *buf, *devname = NULL, *options = NULL; 145 int ret; 146 147 _enter("{%s}", mntpt->d_name.name); 148 149 BUG_ON(!mntpt->d_inode); 150 151 ret = -EINVAL; 152 size = mntpt->d_inode->i_size; 153 if (size > PAGE_SIZE - 1) 154 goto error; 155 156 ret = -ENOMEM; 157 devname = (char *) get_zeroed_page(GFP_KERNEL); 158 if (!devname) 159 goto error; 160 161 options = (char *) get_zeroed_page(GFP_KERNEL); 162 if (!options) 163 goto error; 164 165 /* read the contents of the AFS special symlink */ 166 page = read_mapping_page(mntpt->d_inode->i_mapping, 0, NULL); 167 if (IS_ERR(page)) { 168 ret = PTR_ERR(page); 169 goto error; 170 } 171 172 ret = -EIO; 173 if (PageError(page)) 174 goto error; 175 176 buf = kmap_atomic(page, KM_USER0); 177 memcpy(devname, buf, size); 178 kunmap_atomic(buf, KM_USER0); 179 page_cache_release(page); 180 page = NULL; 181 182 /* work out what options we want */ 183 super = AFS_FS_S(mntpt->d_sb); 184 memcpy(options, "cell=", 5); 185 strcpy(options + 5, super->volume->cell->name); 186 if (super->volume->type == AFSVL_RWVOL) 187 strcat(options, ",rwpath"); 188 189 /* try and do the mount */ 190 _debug("--- attempting mount %s -o %s ---", devname, options); 191 mnt = vfs_kern_mount(&afs_fs_type, 0, devname, options); 192 _debug("--- mount result %p ---", mnt); 193 194 free_page((unsigned long) devname); 195 free_page((unsigned long) options); 196 _leave(" = %p", mnt); 197 return mnt; 198 199 error: 200 if (page) 201 page_cache_release(page); 202 if (devname) 203 free_page((unsigned long) devname); 204 if (options) 205 free_page((unsigned long) options); 206 _leave(" = %d", ret); 207 return ERR_PTR(ret); 208 } 209 210 /* 211 * follow a link from a mountpoint directory, thus causing it to be mounted 212 */ 213 static void *afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd) 214 { 215 struct vfsmount *newmnt; 216 int err; 217 218 _enter("%p{%s},{%s:%p{%s},}", 219 dentry, 220 dentry->d_name.name, 221 nd->path.mnt->mnt_devname, 222 dentry, 223 nd->path.dentry->d_name.name); 224 225 dput(nd->path.dentry); 226 nd->path.dentry = dget(dentry); 227 228 newmnt = afs_mntpt_do_automount(nd->path.dentry); 229 if (IS_ERR(newmnt)) { 230 path_put(&nd->path); 231 return (void *)newmnt; 232 } 233 234 mntget(newmnt); 235 err = do_add_mount(newmnt, &nd->path, MNT_SHRINKABLE, &afs_vfsmounts); 236 switch (err) { 237 case 0: 238 path_put(&nd->path); 239 nd->path.mnt = newmnt; 240 nd->path.dentry = dget(newmnt->mnt_root); 241 schedule_delayed_work(&afs_mntpt_expiry_timer, 242 afs_mntpt_expiry_timeout * HZ); 243 break; 244 case -EBUSY: 245 /* someone else made a mount here whilst we were busy */ 246 while (d_mountpoint(nd->path.dentry) && 247 follow_down(&nd->path.mnt, &nd->path.dentry)) 248 ; 249 err = 0; 250 default: 251 mntput(newmnt); 252 break; 253 } 254 255 _leave(" = %d", err); 256 return ERR_PTR(err); 257 } 258 259 /* 260 * handle mountpoint expiry timer going off 261 */ 262 static void afs_mntpt_expiry_timed_out(struct work_struct *work) 263 { 264 _enter(""); 265 266 if (!list_empty(&afs_vfsmounts)) { 267 mark_mounts_for_expiry(&afs_vfsmounts); 268 schedule_delayed_work(&afs_mntpt_expiry_timer, 269 afs_mntpt_expiry_timeout * HZ); 270 } 271 272 _leave(""); 273 } 274 275 /* 276 * kill the AFS mountpoint timer if it's still running 277 */ 278 void afs_mntpt_kill_timer(void) 279 { 280 _enter(""); 281 282 ASSERT(list_empty(&afs_vfsmounts)); 283 cancel_delayed_work(&afs_mntpt_expiry_timer); 284 flush_scheduled_work(); 285 } 286