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