Set the recovery directory via /proc/fs/nfsd/nfs4recoverydir.
It may be changed any time, but is used only on startup.
Signed-off-by: Andy Adamson <andros@citi.umich.edu>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Signed-off-by: Neil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
 #define NFSDDBG_FACILITY                NFSDDBG_PROC
 
 /* Globals */
-char recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
 static struct nameidata rec_dir;
 static int rec_dir_init = 0;
 
 
 #include <linux/nfs4.h>
 #include <linux/nfsd/state.h>
 #include <linux/nfsd/xdr4.h>
+#include <linux/namei.h>
 
 #define NFSDDBG_FACILITY                NFSDDBG_PROC
 
 static struct nfs4_stateid * find_stateid(stateid_t *stid, int flags);
 static struct nfs4_delegation * find_delegation_stateid(struct inode *ino, stateid_t *stid);
 static void release_stateid_lockowners(struct nfs4_stateid *open_stp);
-extern char recovery_dirname[];
+static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery";
+static void nfs4_set_recdir(char *recdir);
 
 /* Locking:
  *
 {
        int status;
 
-       nfsd4_init_recdir(recovery_dirname);
+       nfs4_lock_state();
+       nfsd4_init_recdir(user_recovery_dirname);
        status = nfsd4_recdir_load();
+       nfs4_unlock_state();
        if (status)
                printk("NFSD: Failure reading reboot recovery data\n");
 }
        nfs4_unlock_state();
 }
 
+static void
+nfs4_set_recdir(char *recdir)
+{
+       nfs4_lock_state();
+       strcpy(user_recovery_dirname, recdir);
+       nfs4_unlock_state();
+}
+
+/*
+ * Change the NFSv4 recovery directory to recdir.
+ */
+int
+nfs4_reset_recoverydir(char *recdir)
+{
+       int status;
+       struct nameidata nd;
+
+       status = path_lookup(recdir, LOOKUP_FOLLOW, &nd);
+       if (status)
+               return status;
+       status = -ENOTDIR;
+       if (S_ISDIR(nd.dentry->d_inode->i_mode)) {
+               nfs4_set_recdir(recdir);
+               status = 0;
+       }
+       path_release(&nd);
+       return status;
+}
+
 /*
  * Called when leasetime is changed.
  *
 
        NFSD_Fh,
        NFSD_Threads,
        NFSD_Leasetime,
+       NFSD_RecoveryDir,
 };
 
 /*
 static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
 static ssize_t write_threads(struct file *file, char *buf, size_t size);
 static ssize_t write_leasetime(struct file *file, char *buf, size_t size);
+static ssize_t write_recoverydir(struct file *file, char *buf, size_t size);
 
 static ssize_t (*write_op[])(struct file *, char *, size_t) = {
        [NFSD_Svc] = write_svc,
        [NFSD_Fh] = write_filehandle,
        [NFSD_Threads] = write_threads,
        [NFSD_Leasetime] = write_leasetime,
+       [NFSD_RecoveryDir] = write_recoverydir,
 };
 
 static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos)
        return strlen(buf);
 }
 
+static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
+{
+       char *mesg = buf;
+       char *recdir;
+       int len, status;
+
+       if (size > PATH_MAX || buf[size-1] != '\n')
+               return -EINVAL;
+       buf[size-1] = 0;
+
+       recdir = mesg;
+       len = qword_get(&mesg, recdir, size);
+       if (len <= 0)
+               return -EINVAL;
+
+       status = nfs4_reset_recoverydir(recdir);
+       return strlen(buf);
+}
+
 /*----------------------------------------------------------------------------*/
 /*
  *     populating the filesystem.
                [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
 #ifdef CONFIG_NFSD_V4
                [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
+               [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR},
 #endif
                /* last one */ {""}
        };
 
 void nfs4_state_shutdown(void);
 time_t nfs4_lease_time(void);
 void nfs4_reset_lease(time_t leasetime);
+int nfs4_reset_recoverydir(char *recdir);
 #else
 static inline void nfs4_state_init(void){};
 static inline int nfs4_state_start(void){return 0;}
 static inline void nfs4_state_shutdown(void){}
 static inline time_t nfs4_lease_time(void){return 0;}
 static inline void nfs4_reset_lease(time_t leasetime){}
+static inline int nfs4_reset_recoverydir(char *recdir) {return 0;}
 #endif
 
 /*