1 /* 2 * Copyright (c) 2004 The Regents of the University of Michigan. 3 * All rights reserved. 4 * 5 * Andy Adamson <andros@citi.umich.edu> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 21 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 22 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 27 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 * 32 */ 33 34 #include <linux/file.h> 35 #include <linux/slab.h> 36 #include <linux/namei.h> 37 #include <linux/crypto.h> 38 #include <linux/sched.h> 39 40 #include "nfsd.h" 41 #include "state.h" 42 #include "vfs.h" 43 44 #define NFSDDBG_FACILITY NFSDDBG_PROC 45 46 /* Globals */ 47 static struct file *rec_file; 48 static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery"; 49 50 static int 51 nfs4_save_creds(const struct cred **original_creds) 52 { 53 struct cred *new; 54 55 new = prepare_creds(); 56 if (!new) 57 return -ENOMEM; 58 59 new->fsuid = 0; 60 new->fsgid = 0; 61 *original_creds = override_creds(new); 62 put_cred(new); 63 return 0; 64 } 65 66 static void 67 nfs4_reset_creds(const struct cred *original) 68 { 69 revert_creds(original); 70 } 71 72 static void 73 md5_to_hex(char *out, char *md5) 74 { 75 int i; 76 77 for (i=0; i<16; i++) { 78 unsigned char c = md5[i]; 79 80 *out++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1); 81 *out++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1); 82 } 83 *out = '\0'; 84 } 85 86 __be32 87 nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname) 88 { 89 struct xdr_netobj cksum; 90 struct hash_desc desc; 91 struct scatterlist sg; 92 __be32 status = nfserr_jukebox; 93 94 dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n", 95 clname->len, clname->data); 96 desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP; 97 desc.tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); 98 if (IS_ERR(desc.tfm)) 99 goto out_no_tfm; 100 cksum.len = crypto_hash_digestsize(desc.tfm); 101 cksum.data = kmalloc(cksum.len, GFP_KERNEL); 102 if (cksum.data == NULL) 103 goto out; 104 105 sg_init_one(&sg, clname->data, clname->len); 106 107 if (crypto_hash_digest(&desc, &sg, sg.length, cksum.data)) 108 goto out; 109 110 md5_to_hex(dname, cksum.data); 111 112 status = nfs_ok; 113 out: 114 kfree(cksum.data); 115 crypto_free_hash(desc.tfm); 116 out_no_tfm: 117 return status; 118 } 119 120 void nfsd4_create_clid_dir(struct nfs4_client *clp) 121 { 122 const struct cred *original_cred; 123 char *dname = clp->cl_recdir; 124 struct dentry *dir, *dentry; 125 int status; 126 127 dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname); 128 129 if (clp->cl_firststate) 130 return; 131 clp->cl_firststate = 1; 132 if (!rec_file) 133 return; 134 status = nfs4_save_creds(&original_cred); 135 if (status < 0) 136 return; 137 138 dir = rec_file->f_path.dentry; 139 /* lock the parent */ 140 mutex_lock(&dir->d_inode->i_mutex); 141 142 dentry = lookup_one_len(dname, dir, HEXDIR_LEN-1); 143 if (IS_ERR(dentry)) { 144 status = PTR_ERR(dentry); 145 goto out_unlock; 146 } 147 if (dentry->d_inode) 148 /* 149 * In the 4.1 case, where we're called from 150 * reclaim_complete(), records from the previous reboot 151 * may still be left, so this is OK. 152 * 153 * In the 4.0 case, we should never get here; but we may 154 * as well be forgiving and just succeed silently. 155 */ 156 goto out_put; 157 status = mnt_want_write_file(rec_file); 158 if (status) 159 goto out_put; 160 status = vfs_mkdir(dir->d_inode, dentry, S_IRWXU); 161 mnt_drop_write_file(rec_file); 162 out_put: 163 dput(dentry); 164 out_unlock: 165 mutex_unlock(&dir->d_inode->i_mutex); 166 if (status == 0) 167 vfs_fsync(rec_file, 0); 168 else 169 printk(KERN_ERR "NFSD: failed to write recovery record" 170 " (err %d); please check that %s exists" 171 " and is writeable", status, 172 user_recovery_dirname); 173 nfs4_reset_creds(original_cred); 174 } 175 176 typedef int (recdir_func)(struct dentry *, struct dentry *); 177 178 struct name_list { 179 char name[HEXDIR_LEN]; 180 struct list_head list; 181 }; 182 183 static int 184 nfsd4_build_namelist(void *arg, const char *name, int namlen, 185 loff_t offset, u64 ino, unsigned int d_type) 186 { 187 struct list_head *names = arg; 188 struct name_list *entry; 189 190 if (namlen != HEXDIR_LEN - 1) 191 return 0; 192 entry = kmalloc(sizeof(struct name_list), GFP_KERNEL); 193 if (entry == NULL) 194 return -ENOMEM; 195 memcpy(entry->name, name, HEXDIR_LEN - 1); 196 entry->name[HEXDIR_LEN - 1] = '\0'; 197 list_add(&entry->list, names); 198 return 0; 199 } 200 201 static int 202 nfsd4_list_rec_dir(recdir_func *f) 203 { 204 const struct cred *original_cred; 205 struct dentry *dir = rec_file->f_path.dentry; 206 LIST_HEAD(names); 207 int status; 208 209 status = nfs4_save_creds(&original_cred); 210 if (status < 0) 211 return status; 212 213 status = vfs_llseek(rec_file, 0, SEEK_SET); 214 if (status < 0) { 215 nfs4_reset_creds(original_cred); 216 return status; 217 } 218 219 status = vfs_readdir(rec_file, nfsd4_build_namelist, &names); 220 mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); 221 while (!list_empty(&names)) { 222 struct name_list *entry; 223 entry = list_entry(names.next, struct name_list, list); 224 if (!status) { 225 struct dentry *dentry; 226 dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1); 227 if (IS_ERR(dentry)) { 228 status = PTR_ERR(dentry); 229 break; 230 } 231 status = f(dir, dentry); 232 dput(dentry); 233 } 234 list_del(&entry->list); 235 kfree(entry); 236 } 237 mutex_unlock(&dir->d_inode->i_mutex); 238 nfs4_reset_creds(original_cred); 239 return status; 240 } 241 242 static int 243 nfsd4_unlink_clid_dir(char *name, int namlen) 244 { 245 struct dentry *dir, *dentry; 246 int status; 247 248 dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s\n", namlen, name); 249 250 dir = rec_file->f_path.dentry; 251 mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); 252 dentry = lookup_one_len(name, dir, namlen); 253 if (IS_ERR(dentry)) { 254 status = PTR_ERR(dentry); 255 goto out_unlock; 256 } 257 status = -ENOENT; 258 if (!dentry->d_inode) 259 goto out; 260 status = vfs_rmdir(dir->d_inode, dentry); 261 out: 262 dput(dentry); 263 out_unlock: 264 mutex_unlock(&dir->d_inode->i_mutex); 265 return status; 266 } 267 268 void 269 nfsd4_remove_clid_dir(struct nfs4_client *clp) 270 { 271 const struct cred *original_cred; 272 int status; 273 274 if (!rec_file || !clp->cl_firststate) 275 return; 276 277 status = mnt_want_write_file(rec_file); 278 if (status) 279 goto out; 280 clp->cl_firststate = 0; 281 282 status = nfs4_save_creds(&original_cred); 283 if (status < 0) 284 goto out; 285 286 status = nfsd4_unlink_clid_dir(clp->cl_recdir, HEXDIR_LEN-1); 287 nfs4_reset_creds(original_cred); 288 if (status == 0) 289 vfs_fsync(rec_file, 0); 290 mnt_drop_write_file(rec_file); 291 out: 292 if (status) 293 printk("NFSD: Failed to remove expired client state directory" 294 " %.*s\n", HEXDIR_LEN, clp->cl_recdir); 295 return; 296 } 297 298 static int 299 purge_old(struct dentry *parent, struct dentry *child) 300 { 301 int status; 302 303 if (nfs4_has_reclaimed_state(child->d_name.name, false)) 304 return 0; 305 306 status = vfs_rmdir(parent->d_inode, child); 307 if (status) 308 printk("failed to remove client recovery directory %s\n", 309 child->d_name.name); 310 /* Keep trying, success or failure: */ 311 return 0; 312 } 313 314 void 315 nfsd4_recdir_purge_old(void) { 316 int status; 317 318 if (!rec_file) 319 return; 320 status = mnt_want_write_file(rec_file); 321 if (status) 322 goto out; 323 status = nfsd4_list_rec_dir(purge_old); 324 if (status == 0) 325 vfs_fsync(rec_file, 0); 326 mnt_drop_write_file(rec_file); 327 out: 328 if (status) 329 printk("nfsd4: failed to purge old clients from recovery" 330 " directory %s\n", rec_file->f_path.dentry->d_name.name); 331 } 332 333 static int 334 load_recdir(struct dentry *parent, struct dentry *child) 335 { 336 if (child->d_name.len != HEXDIR_LEN - 1) { 337 printk("nfsd4: illegal name %s in recovery directory\n", 338 child->d_name.name); 339 /* Keep trying; maybe the others are OK: */ 340 return 0; 341 } 342 nfs4_client_to_reclaim(child->d_name.name); 343 return 0; 344 } 345 346 int 347 nfsd4_recdir_load(void) { 348 int status; 349 350 if (!rec_file) 351 return 0; 352 353 status = nfsd4_list_rec_dir(load_recdir); 354 if (status) 355 printk("nfsd4: failed loading clients from recovery" 356 " directory %s\n", rec_file->f_path.dentry->d_name.name); 357 return status; 358 } 359 360 /* 361 * Hold reference to the recovery directory. 362 */ 363 364 void 365 nfsd4_init_recdir() 366 { 367 const struct cred *original_cred; 368 int status; 369 370 printk("NFSD: Using %s as the NFSv4 state recovery directory\n", 371 user_recovery_dirname); 372 373 BUG_ON(rec_file); 374 375 status = nfs4_save_creds(&original_cred); 376 if (status < 0) { 377 printk("NFSD: Unable to change credentials to find recovery" 378 " directory: error %d\n", 379 status); 380 return; 381 } 382 383 rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0); 384 if (IS_ERR(rec_file)) { 385 printk("NFSD: unable to find recovery directory %s\n", 386 user_recovery_dirname); 387 rec_file = NULL; 388 } 389 390 nfs4_reset_creds(original_cred); 391 } 392 393 void 394 nfsd4_shutdown_recdir(void) 395 { 396 if (!rec_file) 397 return; 398 fput(rec_file); 399 rec_file = NULL; 400 } 401 402 /* 403 * Change the NFSv4 recovery directory to recdir. 404 */ 405 int 406 nfs4_reset_recoverydir(char *recdir) 407 { 408 int status; 409 struct path path; 410 411 status = kern_path(recdir, LOOKUP_FOLLOW, &path); 412 if (status) 413 return status; 414 status = -ENOTDIR; 415 if (S_ISDIR(path.dentry->d_inode->i_mode)) { 416 strcpy(user_recovery_dirname, recdir); 417 status = 0; 418 } 419 path_put(&path); 420 return status; 421 } 422 423 char * 424 nfs4_recoverydir(void) 425 { 426 return user_recovery_dirname; 427 } 428