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