diff options
Diffstat (limited to 'fs')
70 files changed, 1046 insertions, 919 deletions
diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 873802de21c..fbb12dadba8 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -82,7 +82,7 @@ static match_table_t tokens = { static void v9fs_parse_options(struct v9fs_session_info *v9ses) { - char *options = v9ses->options; + char *options; substring_t args[MAX_OPT_ARGS]; char *p; int option; @@ -96,9 +96,10 @@ static void v9fs_parse_options(struct v9fs_session_info *v9ses) v9ses->cache = 0; v9ses->trans = v9fs_default_trans(); - if (!options) + if (!v9ses->options) return; + options = kstrdup(v9ses->options, GFP_KERNEL); while ((p = strsep(&options, ",")) != NULL) { int token; if (!*p) @@ -162,12 +163,14 @@ static void v9fs_parse_options(struct v9fs_session_info *v9ses) if (*e != '\0') v9ses->uid = ~0; } + kfree(s); break; default: continue; } } + kfree(options); } /** diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 175b4d9bf3f..23581bcb599 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -687,10 +687,10 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, retval = p9_client_wstat(oldfid, &wstat); clunk_newdir: - p9_client_clunk(olddirfid); + p9_client_clunk(newdirfid); clunk_olddir: - p9_client_clunk(newdirfid); + p9_client_clunk(olddirfid); done: return retval; diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index bb0cef9a6b8..678c02f1ae2 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -119,6 +119,7 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags, P9_DPRINTK(P9_DEBUG_VFS, " \n"); + st = NULL; v9ses = kzalloc(sizeof(struct v9fs_session_info), GFP_KERNEL); if (!v9ses) return -ENOMEM; @@ -164,10 +165,12 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags, root->d_inode->i_ino = v9fs_qid2ino(&st->qid); v9fs_stat2inode(st, root->d_inode, sb); v9fs_fid_add(root, fid); + kfree(st); return simple_set_mnt(mnt, sb); error: + kfree(st); if (fid) p9_client_clunk(fid); diff --git a/fs/Kconfig b/fs/Kconfig index e431c38a726..429a0022850 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -504,7 +504,7 @@ config INOTIFY including multiple file events, one-shot support, and unmount notification. - For more information, see Documentation/filesystems/inotify.txt + For more information, see <file:Documentation/filesystems/inotify.txt> If unsure, say Y. @@ -518,7 +518,7 @@ config INOTIFY_USER directories via a single open fd. Events are read from the file descriptor, which is also select()- and poll()-able. - For more information, see Documentation/filesystems/inotify.txt + For more information, see <file:Documentation/filesystems/inotify.txt> If unsure, say Y. @@ -1089,7 +1089,7 @@ config ECRYPT_FS depends on EXPERIMENTAL && KEYS && CRYPTO && NET help Encrypted filesystem that operates on the VFS layer. See - <file:Documentation/ecryptfs.txt> to learn more about + <file:Documentation/filesystems/ecryptfs.txt> to learn more about eCryptfs. Userspace components are required and can be obtained from <http://ecryptfs.sf.net>. diff --git a/fs/afs/vlocation.c b/fs/afs/vlocation.c index 7b4bbe48112..849fc3160cb 100644 --- a/fs/afs/vlocation.c +++ b/fs/afs/vlocation.c @@ -382,7 +382,7 @@ struct afs_vlocation *afs_vlocation_lookup(struct afs_cell *cell, cell->name, key_serial(key), (int) namesz, (int) namesz, name, namesz); - if (namesz > sizeof(vl->vldb.name)) { + if (namesz >= sizeof(vl->vldb.name)) { _leave(" = -ENAMETOOLONG"); return ERR_PTR(-ENAMETOOLONG); } diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 62357d228c0..2a21dc66f0d 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -103,7 +103,7 @@ extern int cifs_ioctl(struct inode *inode, struct file *filep, unsigned int command, unsigned long arg); #ifdef CONFIG_CIFS_EXPERIMENTAL -extern struct export_operations cifs_export_ops; +extern const struct export_operations cifs_export_ops; #endif /* EXPERIMENTAL */ #define CIFS_VERSION "1.52" diff --git a/fs/cifs/export.c b/fs/cifs/export.c index d614b91caec..75949d6a5f1 100644 --- a/fs/cifs/export.c +++ b/fs/cifs/export.c @@ -53,7 +53,7 @@ static struct dentry *cifs_get_parent(struct dentry *dentry) return ERR_PTR(-EACCES); } -struct export_operations cifs_export_ops = { +const struct export_operations cifs_export_ops = { .get_parent = cifs_get_parent, /* Following five export operations are unneeded so far and can default: .get_dentry = diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index a4284ccac1f..bd26e4cbb99 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -322,7 +322,7 @@ static int dev_ifname32(unsigned int fd, unsigned int cmd, unsigned long arg) int err; uifr = compat_alloc_user_space(sizeof(struct ifreq)); - if (copy_in_user(uifr, compat_ptr(arg), sizeof(struct ifreq32))); + if (copy_in_user(uifr, compat_ptr(arg), sizeof(struct ifreq32))) return -EFAULT; err = sys_ioctl(fd, SIOCGIFNAME, (unsigned long)uifr); diff --git a/fs/dcache.c b/fs/dcache.c index 2bb3f7ac683..d9ca1e5ceb9 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1479,6 +1479,8 @@ static void switch_names(struct dentry *dentry, struct dentry *target) * dentry:internal, target:external. Steal target's * storage and make target internal. */ + memcpy(target->d_iname, dentry->d_name.name, + dentry->d_name.len + 1); dentry->d_name.name = target->d_name.name; target->d_name.name = target->d_iname; } diff --git a/fs/dlm/lowcomms.c b/fs/dlm/lowcomms.c index 58bf3f5cdbe..e9923ca9c2d 100644 --- a/fs/dlm/lowcomms.c +++ b/fs/dlm/lowcomms.c @@ -1062,7 +1062,7 @@ static int sctp_listen_for_all(void) subscribe.sctp_shutdown_event = 1; subscribe.sctp_partial_delivery_event = 1; - result = kernel_setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + result = kernel_setsockopt(sock, SOL_SOCKET, SO_RCVBUFFORCE, (char *)&bufsize, sizeof(bufsize)); if (result) log_print("Error increasing buffer space on socket %d", result); @@ -1454,10 +1454,6 @@ int dlm_lowcomms_start(void) if (!con_cache) goto out; - /* Set some sysctl minima */ - if (sysctl_rmem_max < NEEDED_RMEM) - sysctl_rmem_max = NEEDED_RMEM; - /* Start listening */ if (dlm_config.ci_protocol == 0) error = tcp_listen_for_all(); diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 1ae90ef2c74..bbed2fd40fd 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -115,11 +115,29 @@ static int ecryptfs_calculate_md5(char *dst, } crypt_stat->hash_tfm = desc.tfm; } - crypto_hash_init(&desc); - crypto_hash_update(&desc, &sg, len); - crypto_hash_final(&desc, dst); - mutex_unlock(&crypt_stat->cs_hash_tfm_mutex); + rc = crypto_hash_init(&desc); + if (rc) { + printk(KERN_ERR + "%s: Error initializing crypto hash; rc = [%d]\n", + __FUNCTION__, rc); + goto out; + } + rc = crypto_hash_update(&desc, &sg, len); + if (rc) { + printk(KERN_ERR + "%s: Error updating crypto hash; rc = [%d]\n", + __FUNCTION__, rc); + goto out; + } + rc = crypto_hash_final(&desc, dst); + if (rc) { + printk(KERN_ERR + "%s: Error finalizing crypto hash; rc = [%d]\n", + __FUNCTION__, rc); + goto out; + } out: + mutex_unlock(&crypt_stat->cs_hash_tfm_mutex); return rc; } @@ -279,13 +297,13 @@ int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg, int offset; int remainder_of_page; + sg_init_table(sg, sg_size); + while (size > 0 && i < sg_size) { pg = virt_to_page(addr); offset = offset_in_page(addr); - if (sg) { - sg[i].page = pg; - sg[i].offset = offset; - } + if (sg) + sg_set_page(&sg[i], pg, 0, offset); remainder_of_page = PAGE_CACHE_SIZE - offset; if (size >= remainder_of_page) { if (sg) @@ -504,7 +522,6 @@ int ecryptfs_encrypt_page(struct page *page) "\n", rc); goto out; } - extent_offset++; } out: kfree(enc_extent_virt); @@ -640,7 +657,6 @@ int ecryptfs_decrypt_page(struct page *page) "rc = [%d]\n", __FUNCTION__, rc); goto out; } - extent_offset++; } out: kfree(enc_extent_virt); @@ -713,12 +729,11 @@ ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, { struct scatterlist src_sg, dst_sg; - src_sg.page = src_page; - src_sg.offset = src_offset; - src_sg.length = size; - dst_sg.page = dst_page; - dst_sg.offset = dst_offset; - dst_sg.length = size; + sg_init_table(&src_sg, 1); + sg_init_table(&dst_sg, 1); + + sg_set_page(&src_sg, src_page, size, src_offset); + sg_set_page(&dst_sg, dst_page, size, dst_offset); return encrypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv); } @@ -742,12 +757,12 @@ ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, { struct scatterlist src_sg, dst_sg; - src_sg.page = src_page; - src_sg.offset = src_offset; - src_sg.length = size; - dst_sg.page = dst_page; - dst_sg.offset = dst_offset; - dst_sg.length = size; + sg_init_table(&src_sg, 1); + sg_set_page(&src_sg, src_page, size, src_offset); + + sg_init_table(&dst_sg, 1); + sg_set_page(&dst_sg, dst_page, size, dst_offset); + return decrypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv); } diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index 89d9710dd63..263fed88c0c 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -1040,6 +1040,9 @@ decrypt_passphrase_encrypted_session_key(struct ecryptfs_auth_tok *auth_tok, }; int rc = 0; + sg_init_table(&dst_sg, 1); + sg_init_table(&src_sg, 1); + if (unlikely(ecryptfs_verbosity > 0)) { ecryptfs_printk( KERN_DEBUG, "Session key encryption key (size [%d]):\n", diff --git a/fs/efs/namei.c b/fs/efs/namei.c index 5276b19423c..f7f407075be 100644 --- a/fs/efs/namei.c +++ b/fs/efs/namei.c @@ -10,6 +10,8 @@ #include <linux/string.h> #include <linux/efs_fs.h> #include <linux/smp_lock.h> +#include <linux/exportfs.h> + static efs_ino_t efs_find_entry(struct inode *inode, const char *name, int len) { struct buffer_head *bh; @@ -75,13 +77,10 @@ struct dentry *efs_lookup(struct inode *dir, struct dentry *dentry, struct namei return NULL; } -struct dentry *efs_get_dentry(struct super_block *sb, void *vobjp) +static struct inode *efs_nfs_get_inode(struct super_block *sb, u64 ino, + u32 generation) { - __u32 *objp = vobjp; - unsigned long ino = objp[0]; - __u32 generation = objp[1]; struct inode *inode; - struct dentry *result; if (ino == 0) return ERR_PTR(-ESTALE); @@ -91,20 +90,25 @@ struct dentry *efs_get_dentry(struct super_block *sb, void *vobjp) if (is_bad_inode(inode) || (generation && inode->i_generation != generation)) { - result = ERR_PTR(-ESTALE); - goto out_iput; + iput(inode); + return ERR_PTR(-ESTALE); } - result = d_alloc_anon(inode); - if (!result) { - result = ERR_PTR(-ENOMEM); - goto out_iput; - } - return result; + return inode; +} - out_iput: - iput(inode); - return result; +struct dentry *efs_fh_to_dentry(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_dentry(sb, fid, fh_len, fh_type, + efs_nfs_get_inode); +} + +struct dentry *efs_fh_to_parent(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_parent(sb, fid, fh_len, fh_type, + efs_nfs_get_inode); } struct dentry *efs_get_parent(struct dentry *child) diff --git a/fs/efs/super.c b/fs/efs/super.c index 25d0326c5f1..c79bc627f10 100644 --- a/fs/efs/super.c +++ b/fs/efs/super.c @@ -113,8 +113,9 @@ static const struct super_operations efs_superblock_operations = { .remount_fs = efs_remount, }; -static struct export_operations efs_export_ops = { - .get_dentry = efs_get_dentry, +static const struct export_operations efs_export_ops = { + .fh_to_dentry = efs_fh_to_dentry, + .fh_to_parent = efs_fh_to_parent, .get_parent = efs_get_parent, }; diff --git a/fs/exec.c b/fs/exec.c index 2c942e2d14e..4ccaaa4b13b 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1692,7 +1692,10 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) if (!binfmt || !binfmt->core_dump) goto fail; down_write(&mm->mmap_sem); - if (!get_dumpable(mm)) { + /* + * If another thread got here first, or we are not dumpable, bail out. + */ + if (mm->core_waiters || !get_dumpable(mm)) { up_write(&mm->mmap_sem); goto fail; } @@ -1706,7 +1709,6 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) flag = O_EXCL; /* Stop rewrite attacks */ current->fsuid = 0; /* Dump root private */ } - set_dumpable(mm, 0); retval = coredump_wait(exit_code); if (retval < 0) diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index 8adb32a9387..109ab5e44ec 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c @@ -1,4 +1,13 @@ - +/* + * Copyright (C) Neil Brown 2002 + * Copyright (C) Christoph Hellwig 2007 + * + * This file contains the code mapping from inodes to NFS file handles, + * and for mapping back from file handles to dentries. + * + * For details on why we do all the strange and hairy things in here + * take a look at Documentation/filesystems/Exporting. + */ #include <linux/exportfs.h> #include <linux/fs.h> #include <linux/file.h> @@ -9,32 +18,19 @@ #define dprintk(fmt, args...) do{}while(0) -static int get_name(struct dentry *dentry, char *name, +static int get_name(struct vfsmount *mnt, struct dentry *dentry, char *name, struct dentry *child); -static struct dentry *exportfs_get_dentry(struct super_block *sb, void *obj) +static int exportfs_get_name(struct vfsmount *mnt, struct dentry *dir, + char *name, struct dentry *child) { - struct dentry *result = ERR_PTR(-ESTALE); - - if (sb->s_export_op->get_dentry) { - result = sb->s_export_op->get_dentry(sb, obj); - if (!result) - result = ERR_PTR(-ESTALE); - } - - return result; -} - -static int exportfs_get_name(struct dentry *dir, char *name, - struct dentry *child) -{ - struct export_operations *nop = dir->d_sb->s_export_op; + const struct export_operations *nop = dir->d_sb->s_export_op; if (nop->get_name) return nop->get_name(dir, name, child); else - return get_name(dir, name, child); + return get_name(mnt, dir, name, child); } /* @@ -98,7 +94,7 @@ find_disconnected_root(struct dentry *dentry) * It may already be, as the flag isn't always updated when connection happens. */ static int -reconnect_path(struct super_block *sb, struct dentry *target_dir) +reconnect_path(struct vfsmount *mnt, struct dentry *target_dir) { char nbuf[NAME_MAX+1]; int noprogress = 0; @@ -121,7 +117,7 @@ reconnect_path(struct super_block *sb, struct dentry *target_dir) pd->d_flags &= ~DCACHE_DISCONNECTED; spin_unlock(&pd->d_lock); noprogress = 0; - } else if (pd == sb->s_root) { + } else if (pd == mnt->mnt_sb->s_root) { printk(KERN_ERR "export: Eeek filesystem root is not connected, impossible\n"); spin_lock(&pd->d_lock); pd->d_flags &= ~DCACHE_DISCONNECTED; @@ -147,8 +143,8 @@ reconnect_path(struct super_block *sb, struct dentry *target_dir) struct dentry *npd; mutex_lock(&pd->d_inode->i_mutex); - if (sb->s_export_op->get_parent) - ppd = sb->s_export_op->get_parent(pd); + if (mnt->mnt_sb->s_export_op->get_parent) + ppd = mnt->mnt_sb->s_export_op->get_parent(pd); mutex_unlock(&pd->d_inode->i_mutex); if (IS_ERR(ppd)) { @@ -161,7 +157,7 @@ reconnect_path(struct super_block *sb, struct dentry *target_dir) dprintk("%s: find name of %lu in %lu\n", __FUNCTION__, pd->d_inode->i_ino, ppd->d_inode->i_ino); - err = exportfs_get_name(ppd, nbuf, pd); + err = exportfs_get_name(mnt, ppd, nbuf, pd); if (err) { dput(ppd); dput(pd); @@ -214,125 +210,6 @@ reconnect_path(struct super_block *sb, struct dentry *target_dir) return 0; } -/** - * find_exported_dentry - helper routine to implement export_operations->decode_fh - * @sb: The &super_block identifying the filesystem - * @obj: An opaque identifier of the object to be found - passed to - * get_inode - * @parent: An optional opqaue identifier of the parent of the object. - * @acceptable: A function used to test possible &dentries to see if they are - * acceptable - * @context: A parameter to @acceptable so that it knows on what basis to - * judge. - * - * find_exported_dentry is the central helper routine to enable file systems - * to provide the decode_fh() export_operation. It's main task is to take - * an &inode, find or create an appropriate &dentry structure, and possibly - * splice this into the dcache in the correct place. - * - * The decode_fh() operation provided by the filesystem should call - * find_exported_dentry() with the same parameters that it received except - * that instead of the file handle fragment, pointers to opaque identifiers - * for the object and optionally its parent are passed. The default decode_fh - * routine passes one pointer to the start of the filehandle fragment, and - * one 8 bytes into the fragment. It is expected that most filesystems will - * take this approach, though the offset to the parent identifier may well be - * different. - * - * find_exported_dentry() will call get_dentry to get an dentry pointer from - * the file system. If any &dentry in the d_alias list is acceptable, it will - * be returned. Otherwise find_exported_dentry() will attempt to splice a new - * &dentry into the dcache using get_name() and get_parent() to find the - * appropriate place. - */ - -struct dentry * -find_exported_dentry(struct super_block *sb, void *obj, void *parent, - int (*acceptable)(void *context, struct dentry *de), - void *context) -{ - struct dentry *result, *alias; - int err = -ESTALE; - - /* - * Attempt to find the inode. - */ - result = exportfs_get_dentry(sb, obj); - if (IS_ERR(result)) - return result; - - if (S_ISDIR(result->d_inode->i_mode)) { - if (!(result->d_flags & DCACHE_DISCONNECTED)) { - if (acceptable(context, result)) - return result; - err = -EACCES; - goto err_result; - } - - err = reconnect_path(sb, result); - if (err) - goto err_result; - } else { - struct dentry *target_dir, *nresult; - char nbuf[NAME_MAX+1]; - - alias = find_acceptable_alias(result, acceptable, context); - if (alias) - return alias; - - if (parent == NULL) - goto err_result; - - target_dir = exportfs_get_dentry(sb,parent); - if (IS_ERR(target_dir)) { - err = PTR_ERR(target_dir); - goto err_result; - } - - err = reconnect_path(sb, target_dir); - if (err) { - dput(target_dir); - goto err_result; - } - - /* - * As we weren't after a directory, have one more step to go. - */ - err = exportfs_get_name(target_dir, nbuf, result); - if (!err) { - mutex_lock(&target_dir->d_inode->i_mutex); - nresult = lookup_one_len(nbuf, target_dir, - strlen(nbuf)); - mutex_unlock(&target_dir->d_inode->i_mutex); - if (!IS_ERR(nresult)) { - if (nresult->d_inode) { - dput(result); - result = nresult; - } else - dput(nresult); - } - } - dput(target_dir); - } - - alias = find_acceptable_alias(result, acceptable, context); - if (alias) - return alias; - - /* drat - I just cannot find anything acceptable */ - dput(result); - /* It might be justifiable to return ESTALE here, - * but the filehandle at-least looks reasonable good - * and it may just be a permission problem, so returning - * -EACCESS is safer - */ - return ERR_PTR(-EACCES); - - err_result: - dput(result); - return ERR_PTR(err); -} - struct getdents_callback { char *name; /* name that was found. It already points to a buffer NAME_MAX+1 is size */ @@ -370,8 +247,8 @@ static int filldir_one(void * __buf, const char * name, int len, * calls readdir on the parent until it finds an entry with * the same inode number as the child, and returns that. */ -static int get_name(struct dentry *dentry, char *name, - struct dentry *child) +static int get_name(struct vfsmount *mnt, struct dentry *dentry, + char *name, struct dentry *child) { struct inode *dir = dentry->d_inode; int error; @@ -387,7 +264,7 @@ static int get_name(struct dentry *dentry, char *name, /* * Open the directory ... */ - file = dentry_open(dget(dentry), NULL, O_RDONLY); + file = dentry_open(dget(dentry), mntget(mnt), O_RDONLY); error = PTR_ERR(file); if (IS_ERR(file)) goto out; @@ -434,100 +311,177 @@ out: * can be used to check that it is still valid. It places them in the * filehandle fragment where export_decode_fh expects to find them. */ -static int export_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len, - int connectable) +static int export_encode_fh(struct dentry *dentry, struct fid *fid, + int *max_len, int connectable) { struct inode * inode = dentry->d_inode; int len = *max_len; - int type = 1; + int type = FILEID_INO32_GEN; if (len < 2 || (connectable && len < 4)) return 255; len = 2; - fh[0] = inode->i_ino; - fh[1] = inode->i_generation; + fid->i32.ino = inode->i_ino; + fid->i32.gen = inode->i_generation; if (connectable && !S_ISDIR(inode->i_mode)) { struct inode *parent; spin_lock(&dentry->d_lock); parent = dentry->d_parent->d_inode; - fh[2] = parent->i_ino; - fh[3] = parent->i_generation; + fid->i32.parent_ino = parent->i_ino; + fid->i32.parent_gen = parent->i_generation; spin_unlock(&dentry->d_lock); len = 4; - type = 2; + type = FILEID_INO32_GEN_PARENT; } *max_len = len; return type; } - -/** - * export_decode_fh - default export_operations->decode_fh function - * @sb: The superblock - * @fh: pointer to the file handle fragment - * @fh_len: length of file handle fragment - * @acceptable: function for testing acceptability of dentrys - * @context: context for @acceptable - * - * This is the default decode_fh() function. - * a fileid_type of 1 indicates that the filehandlefragment - * just contains an object identifier understood by get_dentry. - * a fileid_type of 2 says that there is also a directory - * identifier 8 bytes in to the filehandlefragement. - */ -static struct dentry *export_decode_fh(struct super_block *sb, __u32 *fh, int fh_len, - int fileid_type, - int (*acceptable)(void *context, struct dentry *de), - void *context) -{ - __u32 parent[2]; - parent[0] = parent[1] = 0; - if (fh_len < 2 || fileid_type > 2) - return NULL; - if (fileid_type == 2) { - if (fh_len > 2) parent[0] = fh[2]; - if (fh_len > 3) parent[1] = fh[3]; - } - return find_exported_dentry(sb, fh, parent, - acceptable, context); -} - -int exportfs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len, +int exportfs_encode_fh(struct dentry *dentry, struct fid *fid, int *max_len, int connectable) { - struct export_operations *nop = dentry->d_sb->s_export_op; + const struct export_operations *nop = dentry->d_sb->s_export_op; int error; if (nop->encode_fh) - error = nop->encode_fh(dentry, fh, max_len, connectable); + error = nop->encode_fh(dentry, fid->raw, max_len, connectable); else - error = export_encode_fh(dentry, fh, max_len, connectable); + error = export_encode_fh(dentry, fid, max_len, connectable); return error; } EXPORT_SYMBOL_GPL(exportfs_encode_fh); -struct dentry *exportfs_decode_fh(struct vfsmount *mnt, __u32 *fh, int fh_len, - int fileid_type, int (*acceptable)(void *, struct dentry *), - void *context) +struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid, + int fh_len, int fileid_type, + int (*acceptable)(void *, struct dentry *), void *context) { - struct export_operations *nop = mnt->mnt_sb->s_export_op; - struct dentry *result; + const struct export_operations *nop = mnt->mnt_sb->s_export_op; + struct dentry *result, *alias; + int err; - if (nop->decode_fh) { - result = nop->decode_fh(mnt->mnt_sb, fh, fh_len, fileid_type, - acceptable, context); + /* + * Try to get any dentry for the given file handle from the filesystem. + */ + result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type); + if (!result) + result = ERR_PTR(-ESTALE); + if (IS_ERR(result)) + return result; + + if (S_ISDIR(result->d_inode->i_mode)) { + /* + * This request is for a directory. + * + * On the positive side there is only one dentry for each + * directory inode. On the negative side this implies that we + * to ensure our dentry is connected all the way up to the + * filesystem root. + */ + if (result->d_flags & DCACHE_DISCONNECTED) { + err = reconnect_path(mnt, result); + if (err) + goto err_result; + } + + if (!acceptable(context, result)) { + err = -EACCES; + goto err_result; + } + + return result; } else { - result = export_decode_fh(mnt->mnt_sb, fh, fh_len, fileid_type, - acceptable, context); + /* + * It's not a directory. Life is a little more complicated. + */ + struct dentry *target_dir, *nresult; + char nbuf[NAME_MAX+1]; + + /* + * See if either the dentry we just got from the filesystem + * or any alias for it is acceptable. This is always true + * if this filesystem is exported without the subtreecheck + * option. If the filesystem is exported with the subtree + * check option there's a fair chance we need to look at + * the parent directory in the file handle and make sure + * it's connected to the filesystem root. + */ + alias = find_acceptable_alias(result, acceptable, context); + if (alias) + return alias; + + /* + * Try to extract a dentry for the parent directory from the + * file handle. If this fails we'll have to give up. + */ + err = -ESTALE; + if (!nop->fh_to_parent) + goto err_result; + + target_dir = nop->fh_to_parent(mnt->mnt_sb, fid, + fh_len, fileid_type); + if (!target_dir) + goto err_result; + err = PTR_ERR(target_dir); + if (IS_ERR(target_dir)) + goto err_result; + + /* + * And as usual we need to make sure the parent directory is + * connected to the filesystem root. The VFS really doesn't + * like disconnected directories.. + */ + err = reconnect_path(mnt, target_dir); + if (err) { + dput(target_dir); + goto err_result; + } + + /* + * Now that we've got both a well-connected parent and a + * dentry for the inode we're after, make sure that our + * inode is actually connected to the parent. + */ + err = exportfs_get_name(mnt, target_dir, nbuf, result); + if (!err) { + mutex_lock(&target_dir->d_inode->i_mutex); + nresult = lookup_one_len(nbuf, target_dir, + strlen(nbuf)); + mutex_unlock(&target_dir->d_inode->i_mutex); + if (!IS_ERR(nresult)) { + if (nresult->d_inode) { + dput(result); + result = nresult; + } else + dput(nresult); + } + } + + /* + * At this point we are done with the parent, but it's pinned + * by the child dentry anyway. + */ + dput(target_dir); + + /* + * And finally make sure the dentry is actually acceptable + * to NFSD. + */ + alias = find_acceptable_alias(result, acceptable, context); + if (!alias) { + err = -EACCES; + goto err_result; + } + + return alias; } - return result; + err_result: + dput(result); + return ERR_PTR(err); } EXPORT_SYMBOL_GPL(exportfs_decode_fh); -EXPORT_SYMBOL(find_exported_dentry); - MODULE_LICENSE("GPL"); diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index 05d9342bb64..d868e26c15e 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -28,6 +28,24 @@ typedef struct ext2_dir_entry_2 ext2_dirent; +static inline unsigned ext2_rec_len_from_disk(__le16 dlen) +{ + unsigned len = le16_to_cpu(dlen); + + if (len == EXT2_MAX_REC_LEN) + return 1 << 16; + return len; +} + +static inline __le16 ext2_rec_len_to_disk(unsigned len) +{ + if (len == (1 << 16)) + return cpu_to_le16(EXT2_MAX_REC_LEN); + else if (len > (1 << 16)) + BUG(); + return cpu_to_le16(len); +} + /* * ext2 uses block-sized chunks. Arguably, sector-sized ones would be * more robust, but we have what we have @@ -106,7 +124,7 @@ static void ext2_check_page(struct page *page) } for (offs = 0; offs <= limit - EXT2_DIR_REC_LEN(1); offs += rec_len) { p = (ext2_dirent *)(kaddr + offs); - rec_len = le16_to_cpu(p->rec_len); + rec_len = ext2_rec_len_from_disk(p->rec_len); if (rec_len < EXT2_DIR_REC_LEN(1)) goto Eshort; @@ -204,7 +222,8 @@ static inline int ext2_match (int len, const char * const name, */ static inline ext2_dirent *ext2_next_entry(ext2_dirent *p) { - return (ext2_dirent *)((char*)p + le16_to_cpu(p->rec_len)); + return (ext2_dirent *)((char *)p + + ext2_rec_len_from_disk(p->rec_len)); } static inline unsigned @@ -316,7 +335,7 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir) return 0; } } - filp->f_pos += le16_to_cpu(de->rec_len); + filp->f_pos += ext2_rec_len_from_disk(de->rec_len); } ext2_put_page(page); } @@ -425,7 +444,7 @@ void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de, { loff_t pos = page_offset(page) + (char *) de - (char *) page_address(page); - unsigned len = le16_to_cpu(de->rec_len); + unsigned len = ext2_rec_len_from_disk(de->rec_len); int err; lock_page(page); @@ -482,7 +501,7 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode) /* We hit i_size */ name_len = 0; rec_len = chunk_size; - de->rec_len = cpu_to_le16(chunk_size); + de->rec_len = ext2_rec_len_to_disk(chunk_size); de->inode = 0; goto got_it; } @@ -496,7 +515,7 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode) if (ext2_match (namelen, name, de)) goto out_unlock; name_len = EXT2_DIR_REC_LEN(de->name_len); - rec_len = le16_to_cpu(de->rec_len); + rec_len = ext2_rec_len_from_disk(de->rec_len); if (!de->inode && rec_len >= reclen) goto got_it; if (rec_len >= name_len + reclen) @@ -518,8 +537,8 @@ got_it: goto out_unlock; if (de->inode) { ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len); - de1->rec_len = cpu_to_le16(rec_len - name_len); - de->rec_len = cpu_to_le16(name_len); + de1->rec_len = ext2_rec_len_to_disk(rec_len - name_len); + de->rec_len = ext2_rec_len_to_disk(name_len); de = de1; } de->name_len = namelen; @@ -550,7 +569,8 @@ int ext2_delete_entry (struct ext2_dir_entry_2 * dir, struct page * page ) struct inode *inode = mapping->host; char *kaddr = page_address(page); unsigned from = ((char*)dir - kaddr) & ~(ext2_chunk_size(inode)-1); - unsigned to = ((char*)dir - kaddr) + le16_to_cpu(dir->rec_len); + unsigned to = ((char *)dir - kaddr) + + ext2_rec_len_from_disk(dir->rec_len); loff_t pos; ext2_dirent * pde = NULL; ext2_dirent * de = (ext2_dirent *) (kaddr + from); @@ -574,7 +594,7 @@ int ext2_delete_entry (struct ext2_dir_entry_2 * dir, struct page * page ) &page, NULL); BUG_ON(err); if (pde) - pde->rec_len = cpu_to_le16(to - from); + pde->rec_len = ext2_rec_len_to_disk(to - from); dir->inode = 0; err = ext2_commit_chunk(page, pos, to - from); inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; @@ -610,14 +630,14 @@ int ext2_make_empty(struct inode *inode, struct inode *parent) memset(kaddr, 0, chunk_size); de = (struct ext2_dir_entry_2 *)kaddr; de->name_len = 1; - de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(1)); + de->rec_len = ext2_rec_len_to_disk(EXT2_DIR_REC_LEN(1)); memcpy (de->name, ".\0\0", 4); de->inode = cpu_to_le32(inode->i_ino); ext2_set_de_type (de, inode); de = (struct ext2_dir_entry_2 *)(kaddr + EXT2_DIR_REC_LEN(1)); de->name_len = 2; - de->rec_len = cpu_to_le16(chunk_size - EXT2_DIR_REC_LEN(1)); + de->rec_len = ext2_rec_len_to_disk(chunk_size - EXT2_DIR_REC_LEN(1)); de->inode = cpu_to_le32(parent->i_ino); memcpy (de->name, "..\0", 4); ext2_set_de_type (de, inode); diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 77bd5f9262f..154e25f13d7 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -311,13 +311,10 @@ static const struct super_operations ext2_sops = { #endif }; -static struct dentry *ext2_get_dentry(struct super_block *sb, void *vobjp) +static struct inode *ext2_nfs_get_inode(struct super_block *sb, + u64 ino, u32 generation) { - __u32 *objp = vobjp; - unsigned long ino = objp[0]; - __u32 generation = objp[1]; struct inode *inode; - struct dentry *result; if (ino < EXT2_FIRST_INO(sb) && ino != EXT2_ROOT_INO) return ERR_PTR(-ESTALE); @@ -338,15 +335,21 @@ static struct dentry *ext2_get_dentry(struct super_block *sb, void *vobjp) iput(inode); return ERR_PTR(-ESTALE); } - /* now to find a dentry. - * If possible, get a well-connected one - */ - result = d_alloc_anon(inode); - if (!result) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - return result; + return inode; +} + +static struct dentry *ext2_fh_to_dentry(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_dentry(sb, fid, fh_len, fh_type, + ext2_nfs_get_inode); +} + +static struct dentry *ext2_fh_to_parent(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_parent(sb, fid, fh_len, fh_type, + ext2_nfs_get_inode); } /* Yes, most of these are left as NULL!! @@ -354,9 +357,10 @@ static struct dentry *ext2_get_dentry(struct super_block *sb, void *vobjp) * systems, but can be improved upon. * Currently only get_parent is required. */ -static struct export_operations ext2_export_ops = { +static const struct export_operations ext2_export_ops = { + .fh_to_dentry = ext2_fh_to_dentry, + .fh_to_parent = ext2_fh_to_parent, .get_parent = ext2_get_parent, - .get_dentry = ext2_get_dentry, }; static unsigned long get_sb_block(void **data) diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 81868c0bc40..de55da9e28b 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -631,13 +631,10 @@ static int ext3_show_options(struct seq_file *seq, struct vfsmount *vfs) } -static struct dentry *ext3_get_dentry(struct super_block *sb, void *vobjp) +static struct inode *ext3_nfs_get_inode(struct super_block *sb, + u64 ino, u32 generation) { - __u32 *objp = vobjp; - unsigned long ino = objp[0]; - __u32 generation = objp[1]; struct inode *inode; - struct dentry *result; if (ino < EXT3_FIRST_INO(sb) && ino != EXT3_ROOT_INO) return ERR_PTR(-ESTALE); @@ -660,15 +657,22 @@ static struct dentry *ext3_get_dentry(struct super_block *sb, void *vobjp) iput(inode); return ERR_PTR(-ESTALE); } - /* now to find a dentry. - * If possible, get a well-connected one - */ - result = d_alloc_anon(inode); - if (!result) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - return result; + + return inode; +} + +static struct dentry *ext3_fh_to_dentry(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_dentry(sb, fid, fh_len, fh_type, + ext3_nfs_get_inode); +} + +static struct dentry *ext3_fh_to_parent(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_parent(sb, fid, fh_len, fh_type, + ext3_nfs_get_inode); } #ifdef CONFIG_QUOTA @@ -737,9 +741,10 @@ static const struct super_operations ext3_sops = { #endif }; -static struct export_operations ext3_export_ops = { +static const struct export_operations ext3_export_ops = { + .fh_to_dentry = ext3_fh_to_dentry, + .fh_to_parent = ext3_fh_to_parent, .get_parent = ext3_get_parent, - .get_dentry = ext3_get_dentry, }; enum { diff --git a/fs/ext4/super.c b/fs/ext4/super.c index b11e9e2bcd0..8031dc0e24e 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -686,13 +686,10 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs) } -static struct dentry *ext4_get_dentry(struct super_block *sb, void *vobjp) +static struct inode *ext4_nfs_get_inode(struct super_block *sb, + u64 ino, u32 generation) { - __u32 *objp = vobjp; - unsigned long ino = objp[0]; - __u32 generation = objp[1]; struct inode *inode; - struct dentry *result; if (ino < EXT4_FIRST_INO(sb) && ino != EXT4_ROOT_INO) return ERR_PTR(-ESTALE); @@ -715,15 +712,22 @@ static struct dentry *ext4_get_dentry(struct super_block *sb, void *vobjp) iput(inode); return ERR_PTR(-ESTALE); } - /* now to find a dentry. - * If possible, get a well-connected one - */ - result = d_alloc_anon(inode); - if (!result) { - iput(inode); - return ERR_PTR(-ENOMEM); - } - return result; + + return inode; +} + +static struct dentry *ext4_fh_to_dentry(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_dentry(sb, fid, fh_len, fh_type, + ext4_nfs_get_inode); +} + +static struct dentry *ext4_fh_to_parent(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_parent(sb, fid, fh_len, fh_type, + ext4_nfs_get_inode); } #ifdef CONFIG_QUOTA @@ -792,9 +796,10 @@ static const struct super_operations ext4_sops = { #endif }; -static struct export_operations ext4_export_ops = { +static const struct export_operations ext4_export_ops = { + .fh_to_dentry = ext4_fh_to_dentry, + .fh_to_parent = ext4_fh_to_parent, .get_parent = ext4_get_parent, - .get_dentry = ext4_get_dentry, }; enum { diff --git a/fs/fat/inode.c b/fs/fat/inode.c index c0c5e9c55b5..920a576e1c2 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -653,24 +653,15 @@ static const struct super_operations fat_sops = { * of i_logstart is used to store the directory entry offset. */ -static struct dentry * -fat_decode_fh(struct super_block *sb, __u32 *fh, int len, int fhtype, - int (*acceptable)(void *context, struct dentry *de), - void *context) -{ - if (fhtype != 3) - return ERR_PTR(-ESTALE); - if (len < 5) - return ERR_PTR(-ESTALE); - - return sb->s_export_op->find_exported_dentry(sb, fh, NULL, acceptable, context); -} - -static struct dentry *fat_get_dentry(struct super_block *sb, void *inump) +static struct dentry *fat_fh_to_dentry(struct super_block *sb, + struct fid *fid, int fh_len, int fh_type) { struct inode *inode = NULL; struct dentry *result; - __u32 *fh = inump; + u32 *fh = fid->raw; + + if (fh_len < 5 || fh_type != 3) + return NULL; inode = iget(sb, fh[0]); if (!inode || is_bad_inode(inode) || inode->i_generation != fh[1]) { @@ -783,10 +774,9 @@ out: return parent; } -static struct export_operations fat_export_ops = { - .decode_fh = fat_decode_fh, +static const struct export_operations fat_export_ops = { .encode_fh = fat_encode_fh, - .get_dentry = fat_get_dentry, + .fh_to_dentry = fat_fh_to_dentry, .get_parent = fat_get_parent, }; diff --git a/fs/gfs2/ops_export.c b/fs/gfs2/ops_export.c index e2d1347796a..b9da62348a8 100644 --- a/fs/gfs2/ops_export.c +++ b/fs/gfs2/ops_export.c @@ -31,40 +31,6 @@ #define GFS2_LARGE_FH_SIZE 8 #define GFS2_OLD_FH_SIZE 10 -static struct dentry *gfs2_decode_fh(struct super_block *sb, - __u32 *p, - int fh_len, - int fh_type, - int (*acceptable)(void *context, - struct dentry *dentry), - void *context) -{ - __be32 *fh = (__force __be32 *)p; - struct gfs2_inum_host inum, parent; - - memset(&parent, 0, sizeof(struct gfs2_inum)); - - switch (fh_len) { - case GFS2_LARGE_FH_SIZE: - case GFS2_OLD_FH_SIZE: - parent.no_formal_ino = ((u64)be32_to_cpu(fh[4])) << 32; - parent.no_formal_ino |= be32_to_cpu(fh[5]); - parent.no_addr = ((u64)be32_to_cpu(fh[6])) << 32; - parent.no_addr |= be32_to_cpu(fh[7]); - case GFS2_SMALL_FH_SIZE: - inum.no_formal_ino = ((u64)be32_to_cpu(fh[0])) << 32; - inum.no_formal_ino |= be32_to_cpu(fh[1]); - inum.no_addr = ((u64)be32_to_cpu(fh[2])) << 32; - inum.no_addr |= be32_to_cpu(fh[3]); - break; - default: - return NULL; - } - - return gfs2_export_ops.find_exported_dentry(sb, &inum, &parent, - acceptable, context); -} - static int gfs2_encode_fh(struct dentry *dentry, __u32 *p, int *len, int connectable) { @@ -189,10 +155,10 @@ static struct dentry *gfs2_get_parent(struct dentry *child) return dentry; } -static struct dentry *gfs2_get_dentry(struct super_block *sb, void *inum_obj) +static struct dentry *gfs2_get_dentry(struct super_block *sb, + struct gfs2_inum_host *inum) { struct gfs2_sbd *sdp = sb->s_fs_info; - struct gfs2_inum_host *inum = inum_obj; struct gfs2_holder i_gh, ri_gh, rgd_gh; struct gfs2_rgrpd *rgd; struct inode *inode; @@ -289,11 +255,50 @@ fail: return ERR_PTR(error); } -struct export_operations gfs2_export_ops = { - .decode_fh = gfs2_decode_fh, +static struct dentry *gfs2_fh_to_dentry(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + struct gfs2_inum_host this; + __be32 *fh = (__force __be32 *)fid->raw; + + switch (fh_type) { + case GFS2_SMALL_FH_SIZE: + case GFS2_LARGE_FH_SIZE: + case GFS2_OLD_FH_SIZE: + this.no_formal_ino = ((u64)be32_to_cpu(fh[0])) << 32; + this.no_formal_ino |= be32_to_cpu(fh[1]); + this.no_addr = ((u64)be32_to_cpu(fh[2])) << 32; + this.no_addr |= be32_to_cpu(fh[3]); + return gfs2_get_dentry(sb, &this); + default: + return NULL; + } +} + +static struct dentry *gfs2_fh_to_parent(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + struct gfs2_inum_host parent; + __be32 *fh = (__force __be32 *)fid->raw; + + switch (fh_type) { + case GFS2_LARGE_FH_SIZE: + case GFS2_OLD_FH_SIZE: + parent.no_formal_ino = ((u64)be32_to_cpu(fh[4])) << 32; + parent.no_formal_ino |= be32_to_cpu(fh[5]); + parent.no_addr = ((u64)be32_to_cpu(fh[6])) << 32; + parent.no_addr |= be32_to_cpu(fh[7]); + return gfs2_get_dentry(sb, &parent); + default: + return NULL; + } +} + +const struct export_operations gfs2_export_ops = { .encode_fh = gfs2_encode_fh, + .fh_to_dentry = gfs2_fh_to_dentry, + .fh_to_parent = gfs2_fh_to_parent, .get_name = gfs2_get_name, .get_parent = gfs2_get_parent, - .get_dentry = gfs2_get_dentry, }; diff --git a/fs/gfs2/ops_fstype.h b/fs/gfs2/ops_fstype.h index 407029b3b2b..da849051183 100644 --- a/fs/gfs2/ops_fstype.h +++ b/fs/gfs2/ops_fstype.h @@ -14,6 +14,6 @@ extern struct file_system_type gfs2_fs_type; extern struct file_system_type gfs2meta_fs_type; -extern struct export_operations gfs2_export_ops; +extern const struct export_operations gfs2_export_ops; #endif /* __OPS_FSTYPE_DOT_H__ */ diff --git a/fs/ioprio.c b/fs/ioprio.c index d6ff77e8e7e..e4e01bc7f33 100644 --- a/fs/ioprio.c +++ b/fs/ioprio.c @@ -78,6 +78,10 @@ asmlinkage long sys_ioprio_set(int which, int who, int ioprio) if (!capable(CAP_SYS_ADMIN)) return -EPERM; break; + case IOPRIO_CLASS_NONE: + if (data) + return -EINVAL; + break; default: return -EINVAL; } diff --git a/fs/isofs/export.c b/fs/isofs/export.c index 4af856a7fda..29f9753ae5e 100644 --- a/fs/isofs/export.c +++ b/fs/isofs/export.c @@ -42,16 +42,6 @@ isofs_export_iget(struct super_block *sb, return result; } -static struct dentry * -isofs_export_get_dentry(struct super_block *sb, void *vobjp) -{ - __u32 *objp = vobjp; - unsigned long block = objp[0]; - unsigned long offset = objp[1]; - __u32 generation = objp[2]; - return isofs_export_iget(sb, block, offset, generation); -} - /* This function is surprisingly simple. The trick is understanding * that "child" is always a directory. So, to find its parent, you * simply need to find its ".." entry, normalize its block and offset, @@ -182,43 +172,44 @@ isofs_export_encode_fh(struct dentry *dentry, return type; } +struct isofs_fid { + u32 block; + u16 offset; + u16 parent_offset; + u32 generation; + u32 parent_block; + u32 parent_generation; +}; -static struct dentry * -isofs_export_decode_fh(struct super_block *sb, - __u32 *fh32, - int fh_len, - int fileid_type, - int (*acceptable)(void *context, struct dentry *de), - void *context) +static struct dentry *isofs_fh_to_dentry(struct super_block *sb, + struct fid *fid, int fh_len, int fh_type) { - __u16 *fh16 = (__u16*)fh32; - __u32 child[3]; /* The child is what triggered all this. */ - __u32 parent[3]; /* The parent is just along for the ride. */ + struct isofs_fid *ifid = (struct isofs_fid *)fid; - if (fh_len < 3 || fileid_type > 2) + if (fh_len < 3 || fh_type > 2) return NULL; - child[0] = fh32[0]; - child[1] = fh16[2]; /* fh16 [sic] */ - child[2] = fh32[2]; - - parent[0] = 0; - parent[1] = 0; - parent[2] = 0; - if (fileid_type == 2) { - if (fh_len > 2) parent[0] = fh32[3]; - parent[1] = fh16[3]; /* fh16 [sic] */ - if (fh_len > 4) parent[2] = fh32[4]; - } - - return sb->s_export_op->find_exported_dentry(sb, child, parent, - acceptable, context); + return isofs_export_iget(sb, ifid->block, ifid->offset, + ifid->generation); } +static struct dentry *isofs_fh_to_parent(struct super_block *sb, + struct fid *fid, int fh_len, int fh_type) +{ + struct isofs_fid *ifid = (struct isofs_fid *)fid; + + if (fh_type != 2) + return NULL; + + return isofs_export_iget(sb, + fh_len > 2 ? ifid->parent_block : 0, + ifid->parent_offset, + fh_len > 4 ? ifid->parent_generation : 0); +} -struct export_operations isofs_export_ops = { - .decode_fh = isofs_export_decode_fh, +const struct export_operations isofs_export_ops = { .encode_fh = isofs_export_encode_fh, - .get_dentry = isofs_export_get_dentry, + .fh_to_dentry = isofs_fh_to_dentry, + .fh_to_parent = isofs_fh_to_parent, .get_parent = isofs_export_get_parent, }; diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h index a07e67b1ea7..f3213f9f89a 100644 --- a/fs/isofs/isofs.h +++ b/fs/isofs/isofs.h @@ -178,4 +178,4 @@ isofs_normalize_block_and_offset(struct iso_directory_record* de, extern const struct inode_operations isofs_dir_inode_operations; extern const struct file_operations isofs_dir_operations; extern const struct address_space_operations isofs_symlink_aops; -extern struct export_operations isofs_export_ops; +extern const struct export_operations isofs_export_ops; diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c index 8ec9323e830..77fc5838609 100644 --- a/fs/jffs2/acl.c +++ b/fs/jffs2/acl.c @@ -228,11 +228,28 @@ struct posix_acl *jffs2_get_acl(struct inode *inode, int type) return acl; } +static int __jffs2_set_acl(struct inode *inode, int xprefix, struct posix_acl *acl) +{ + char *value = NULL; + size_t size = 0; + int rc; + + if (acl) { + value = jffs2_acl_to_medium(acl, &size); + if (IS_ERR(value)) + return PTR_ERR(value); + } + rc = do_jffs2_setxattr(inode, xprefix, "", value, size, 0); + if (!value && rc == -ENODATA) + rc = 0; + kfree(value); + + return rc; +} + static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl) { struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); - size_t size = 0; - char *value = NULL; int rc, xprefix; if (S_ISLNK(inode->i_mode)) @@ -267,17 +284,7 @@ static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl) default: return -EINVAL; } - if (acl) { - value = jffs2_acl_to_medium(acl, &size); - if (IS_ERR(value)) - return PTR_ERR(value); - } - - rc = do_jffs2_setxattr(inode, xprefix, "", value, size, 0); - if (!value && rc == -ENODATA) - rc = 0; - if (value) - kfree(value); + rc = __jffs2_set_acl(inode, xprefix, acl); if (!rc) { switch(type) { case ACL_TYPE_ACCESS: @@ -312,38 +319,60 @@ int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd) return generic_permission(inode, mask, jffs2_check_acl); } -int jffs2_init_acl(struct inode *inode, struct posix_acl *acl) +int jffs2_init_acl_pre(struct inode *dir_i, struct inode *inode, int *i_mode) { struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); - struct posix_acl *clone; - mode_t mode; - int rc = 0; + struct posix_acl *acl, *clone; + int rc; - f->i_acl_access = JFFS2_ACL_NOT_CACHED; - f->i_acl_default = JFFS2_ACL_NOT_CACHED; + f->i_acl_default = NULL; + f->i_acl_access = NULL; + + if (S_ISLNK(*i_mode)) + return 0; /* Symlink always has no-ACL */ + + acl = jffs2_get_acl(dir_i, ACL_TYPE_DEFAULT); + if (IS_ERR(acl)) + return PTR_ERR(acl); + + if (!acl) { + *i_mode &= ~current->fs->umask; + } else { + if (S_ISDIR(*i_mode)) + jffs2_iset_acl(inode, &f->i_acl_default, acl); - if (acl) { - if (S_ISDIR(inode->i_mode)) { - rc = jffs2_set_acl(inode, ACL_TYPE_DEFAULT, acl); - if (rc) - goto cleanup; - } clone = posix_acl_clone(acl, GFP_KERNEL); - rc = -ENOMEM; if (!clone) - goto cleanup; - mode = inode->i_mode; - rc = posix_acl_create_masq(clone, &mode); - if (rc >= 0) { - inode->i_mode = mode; - if (rc > 0) - rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone); - } + return -ENOMEM; + rc = posix_acl_create_masq(clone, (mode_t *)i_mode); + if (rc < 0) + return rc; + if (rc > 0) + jffs2_iset_acl(inode, &f->i_acl_access, clone); + posix_acl_release(clone); } - cleanup: - posix_acl_release(acl); - return rc; + return 0; +} + +int jffs2_init_acl_post(struct inode *inode) +{ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + int rc; + + if (f->i_acl_default) { + rc = __jffs2_set_acl(inode, JFFS2_XPREFIX_ACL_DEFAULT, f->i_acl_default); + if (rc) + return rc; + } + + if (f->i_acl_access) { + rc = __jffs2_set_acl(inode, JFFS2_XPREFIX_ACL_ACCESS, f->i_acl_access); + if (rc) + return rc; + } + + return 0; } void jffs2_clear_acl(struct jffs2_inode_info *f) diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h index 90a2dbf5905..76c6ebd1acd 100644 --- a/fs/jffs2/acl.h +++ b/fs/jffs2/acl.h @@ -31,7 +31,8 @@ struct jffs2_acl_header { extern struct posix_acl *jffs2_get_acl(struct inode *inode, int type); extern int jffs2_permission(struct inode *, int, struct nameidata *); extern int jffs2_acl_chmod(struct inode *); -extern int jffs2_init_acl(struct inode *, struct posix_acl *); +extern int jffs2_init_acl_pre(struct inode *, struct inode *, int *); +extern int jffs2_init_acl_post(struct inode *); extern void jffs2_clear_acl(struct jffs2_inode_info *); extern struct xattr_handler jffs2_acl_access_xattr_handler; @@ -39,10 +40,11 @@ extern struct xattr_handler jffs2_acl_default_xattr_handler; #else -#define jffs2_get_acl(inode, type) (NULL) -#define jffs2_permission NULL -#define jffs2_acl_chmod(inode) (0) -#define jffs2_init_acl(inode,dir) (0) +#define jffs2_get_acl(inode, type) (NULL) +#define jffs2_permission (NULL) +#define jffs2_acl_chmod(inode) (0) +#define jffs2_init_acl_pre(dir_i,inode,mode) (0) +#define jffs2_init_acl_post(inode) (0) #define jffs2_clear_acl(f) #endif /* CONFIG_JFFS2_FS_POSIX_ACL */ diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 8353eb9c179..787e392ffd4 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -182,7 +182,6 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, struct jffs2_inode_info *f, *dir_f; struct jffs2_sb_info *c; struct inode *inode; - struct posix_acl *acl; int ret; ri = jffs2_alloc_raw_inode(); @@ -193,7 +192,7 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, D1(printk(KERN_DEBUG "jffs2_create()\n")); - inode = jffs2_new_inode(dir_i, mode, ri, &acl); + inode = jffs2_new_inode(dir_i, mode, ri); if (IS_ERR(inode)) { D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n")); @@ -211,14 +210,6 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, ret = jffs2_do_create(c, dir_f, f, ri, dentry->d_name.name, dentry->d_name.len); - - if (ret) - goto fail_acl; - - ret = jffs2_init_security(inode, dir_i); - if (ret) - goto fail_acl; - ret = jffs2_init_acl(inode, acl); if (ret) goto fail; @@ -231,8 +222,6 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages)); return 0; - fail_acl: - posix_acl_release(acl); fail: make_bad_inode(inode); iput(inode); @@ -309,7 +298,6 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char struct jffs2_full_dirent *fd; int namelen; uint32_t alloclen; - struct posix_acl *acl; int ret, targetlen = strlen(target); /* FIXME: If you care. We'd need to use frags for the target @@ -336,7 +324,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char return ret; } - inode = jffs2_new_inode(dir_i, S_IFLNK | S_IRWXUGO, ri, &acl); + inode = jffs2_new_inode(dir_i, S_IFLNK | S_IRWXUGO, ri); if (IS_ERR(inode)) { jffs2_free_raw_inode(ri); @@ -366,7 +354,6 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); - posix_acl_release(acl); return PTR_ERR(fn); } @@ -377,7 +364,6 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); - posix_acl_release(acl); return -ENOMEM; } @@ -395,10 +381,9 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char ret = jffs2_init_security(inode, dir_i); if (ret) { jffs2_clear_inode(inode); - posix_acl_release(acl); return ret; } - ret = jffs2_init_acl(inode, acl); + ret = jffs2_init_acl_post(inode); if (ret) { jffs2_clear_inode(inode); return ret; @@ -476,7 +461,6 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) struct jffs2_full_dirent *fd; int namelen; uint32_t alloclen; - struct posix_acl *acl; int ret; mode |= S_IFDIR; @@ -499,7 +483,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) return ret; } - inode = jffs2_new_inode(dir_i, mode, ri, &acl); + inode = jffs2_new_inode(dir_i, mode, ri); if (IS_ERR(inode)) { jffs2_free_raw_inode(ri); @@ -526,7 +510,6 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); - posix_acl_release(acl); return PTR_ERR(fn); } /* No data here. Only a metadata node, which will be @@ -540,10 +523,9 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) ret = jffs2_init_security(inode, dir_i); if (ret) { jffs2_clear_inode(inode); - posix_acl_release(acl); return ret; } - ret = jffs2_init_acl(inode, acl); + ret = jffs2_init_acl_post(inode); if (ret) { jffs2_clear_inode(inode); return ret; @@ -639,7 +621,6 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de union jffs2_device_node dev; int devlen = 0; uint32_t alloclen; - struct posix_acl *acl; int ret; if (!new_valid_dev(rdev)) @@ -666,7 +647,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de return ret; } - inode = jffs2_new_inode(dir_i, mode, ri, &acl); + inode = jffs2_new_inode(dir_i, mode, ri); if (IS_ERR(inode)) { jffs2_free_raw_inode(ri); @@ -695,7 +676,6 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de up(&f->sem); jffs2_complete_reservation(c); jffs2_clear_inode(inode); - posix_acl_release(acl); return PTR_ERR(fn); } /* No data here. Only a metadata node, which will be @@ -709,10 +689,9 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de ret = jffs2_init_security(inode, dir_i); if (ret) { jffs2_clear_inode(inode); - posix_acl_release(acl); return ret; } - ret = jffs2_init_acl(inode, acl); + ret = jffs2_init_acl_post(inode); if (ret) { jffs2_clear_inode(inode); return ret; diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index 023a17539dd..f9c5dd6f4b6 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -255,7 +255,7 @@ static int jffs2_write_end(struct file *filp, struct address_space *mapping, _whole_ page. This helps to reduce the number of nodes in files which have many short writes, like syslog files. */ - start = aligned_start = 0; + aligned_start = 0; } ri = jffs2_alloc_raw_inode(); @@ -291,14 +291,11 @@ static int jffs2_write_end(struct file *filp, struct address_space *mapping, } /* Adjust writtenlen for the padding we did, so we don't confuse our caller */ - if (writtenlen < (start&3)) - writtenlen = 0; - else - writtenlen -= (start&3); + writtenlen -= min(writtenlen, (start - aligned_start)); if (writtenlen) { - if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) { - inode->i_size = (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen; + if (inode->i_size < pos + writtenlen) { + inode->i_size = pos + writtenlen; inode->i_blocks = (inode->i_size + 511) >> 9; inode->i_ctime = inode->i_mtime = ITIME(je32_to_cpu(ri->ctime)); diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index ed85f9afdbc..d2e06f7ea96 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -402,8 +402,7 @@ void jffs2_write_super (struct super_block *sb) /* jffs2_new_inode: allocate a new inode and inocache, add it to the hash, fill in the raw_inode while you're at it. */ -struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri, - struct posix_acl **acl) +struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri) { struct inode *inode; struct super_block *sb = dir_i->i_sb; @@ -438,19 +437,11 @@ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_i /* POSIX ACLs have to be processed now, at least partly. The umask is only applied if there's no default ACL */ - if (!S_ISLNK(mode)) { - *acl = jffs2_get_acl(dir_i, ACL_TYPE_DEFAULT); - if (IS_ERR(*acl)) { - make_bad_inode(inode); - iput(inode); - inode = (void *)*acl; - *acl = NULL; - return inode; - } - if (!(*acl)) - mode &= ~current->fs->umask; - } else { - *acl = NULL; + ret = jffs2_init_acl_pre(dir_i, inode, &mode); + if (ret) { + make_bad_inode(inode); + iput(inode); + return ERR_PTR(ret); } ret = jffs2_do_new_inode (c, f, mode, ri); if (ret) { diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index f6743a915cf..bf64686cf09 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -173,15 +173,13 @@ int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long); extern const struct inode_operations jffs2_symlink_inode_operations; /* fs.c */ -struct posix_acl; - int jffs2_setattr (struct dentry *, struct iattr *); int jffs2_do_setattr (struct inode *, struct iattr *); void jffs2_read_inode (struct inode *); void jffs2_clear_inode (struct inode *); void jffs2_dirty_inode(struct inode *inode); struct inode *jffs2_new_inode (struct inode *dir_i, int mode, - struct jffs2_raw_inode *ri, struct posix_acl **acl); + struct jffs2_raw_inode *ri); int jffs2_statfs (struct dentry *, struct kstatfs *); void jffs2_write_super (struct super_block *); int jffs2_remount_fs (struct super_block *, int *, char *); diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index 2f5695446d0..147e2cbee9e 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -465,6 +465,14 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str up(&f->sem); jffs2_complete_reservation(c); + + ret = jffs2_init_security(&f->vfs_inode, &dir_f->vfs_inode); + if (ret) + return ret; + ret = jffs2_init_acl_post(&f->vfs_inode); + if (ret) + return ret; + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); diff --git a/fs/jfs/jfs_inode.h b/fs/jfs/jfs_inode.h index f0ec72b263f..8e2cf2cde18 100644 --- a/fs/jfs/jfs_inode.h +++ b/fs/jfs/jfs_inode.h @@ -18,6 +18,8 @@ #ifndef _H_JFS_INODE #define _H_JFS_INODE +struct fid; + extern struct inode *ialloc(struct inode *, umode_t); extern int jfs_fsync(struct file *, struct dentry *, int); extern int jfs_ioctl(struct inode *, struct file *, @@ -32,7 +34,10 @@ extern void jfs_truncate_nolock(struct inode *, loff_t); extern void jfs_free_zero_link(struct inode *); extern struct dentry *jfs_get_parent(struct dentry *dentry); extern void jfs_get_inode_flags(struct jfs_inode_info *); -extern struct dentry *jfs_get_dentry(struct super_block *sb, void *vobjp); +extern struct dentry *jfs_fh_to_dentry(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type); +extern struct dentry *jfs_fh_to_parent(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type); extern void jfs_set_inode_flags(struct inode *); extern int jfs_get_block(struct inode *, sector_t, struct buffer_head *, int); diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 932797ba433..4e0a8493cef 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -20,6 +20,7 @@ #include <linux/fs.h> #include <linux/ctype.h> #include <linux/quotaops.h> +#include <linux/exportfs.h> #include "jfs_incore.h" #include "jfs_superblock.h" #include "jfs_inode.h" @@ -1477,13 +1478,10 @@ static struct dentry *jfs_lookup(struct inode *dip, struct dentry *dentry, struc return dentry; } -struct dentry *jfs_get_dentry(struct super_block *sb, void *vobjp) +static struct inode *jfs_nfs_get_inode(struct super_block *sb, + u64 ino, u32 generation) { - __u32 *objp = vobjp; - unsigned long ino = objp[0]; - __u32 generation = objp[1]; struct inode *inode; - struct dentry *result; if (ino == 0) return ERR_PTR(-ESTALE); @@ -1493,20 +1491,25 @@ struct dentry *jfs_get_dentry(struct super_block *sb, void *vobjp) if (is_bad_inode(inode) || (generation && inode->i_generation != generation)) { - result = ERR_PTR(-ESTALE); - goto out_iput; + iput(inode); + return ERR_PTR(-ESTALE); } - result = d_alloc_anon(inode); - if (!result) { - result = ERR_PTR(-ENOMEM); - goto out_iput; - } - return result; + return inode; +} - out_iput: - iput(inode); - return result; +struct dentry *jfs_fh_to_dentry(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_dentry(sb, fid, fh_len, fh_type, + jfs_nfs_get_inode); +} + +struct dentry *jfs_fh_to_parent(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_parent(sb, fid, fh_len, fh_type, + jfs_nfs_get_inode); } struct dentry *jfs_get_parent(struct dentry *dentry) diff --git a/fs/jfs/super.c b/fs/jfs/super.c index cff60c17194..314bb4ff1ba 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -48,7 +48,7 @@ MODULE_LICENSE("GPL"); static struct kmem_cache * jfs_inode_cachep; static const struct super_operations jfs_super_operations; -static struct export_operations jfs_export_operations; +static const struct export_operations jfs_export_operations; static struct file_system_type jfs_fs_type; #define MAX_COMMIT_THREADS 64 @@ -737,8 +737,9 @@ static const struct super_operations jfs_super_operations = { #endif }; -static struct export_operations jfs_export_operations = { - .get_dentry = jfs_get_dentry, +static const struct export_operations jfs_export_operations = { + .fh_to_dentry = jfs_fh_to_dentry, + .fh_to_parent = jfs_fh_to_parent, .get_parent = jfs_get_parent, }; diff --git a/fs/libfs.c b/fs/libfs.c index ae51481e45e..6e68b700958 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -8,6 +8,7 @@ #include <linux/mount.h> #include <linux/vfs.h> #include <linux/mutex.h> +#include <linux/exportfs.h> #include <asm/uaccess.h> @@ -678,6 +679,93 @@ out: return ret; } +/* + * This is what d_alloc_anon should have been. Once the exportfs + * argument transition has been finished I will update d_alloc_anon + * to this prototype and this wrapper will go away. --hch + */ +static struct dentry *exportfs_d_alloc(struct inode *inode) +{ + struct dentry *dentry; + + if (!inode) + return NULL; + if (IS_ERR(inode)) + return ERR_PTR(PTR_ERR(inode)); + + dentry = d_alloc_anon(inode); + if (!dentry) { + iput(inode); + dentry = ERR_PTR(-ENOMEM); + } + return dentry; +} + +/** + * generic_fh_to_dentry - generic helper for the fh_to_dentry export operation + * @sb: filesystem to do the file handle conversion on + * @fid: file handle to convert + * @fh_len: length of the file handle in bytes + * @fh_type: type of file handle + * @get_inode: filesystem callback to retrieve inode + * + * This function decodes @fid as long as it has one of the well-known + * Linux filehandle types and calls @get_inode on it to retrieve the + * inode for the object specified in the file handle. + */ +struct dentry *generic_fh_to_dentry(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type, struct inode *(*get_inode) + (struct super_block *sb, u64 ino, u32 gen)) +{ + struct inode *inode = NULL; + + if (fh_len < 2) + return NULL; + + switch (fh_type) { + case FILEID_INO32_GEN: + case FILEID_INO32_GEN_PARENT: + inode = get_inode(sb, fid->i32.ino, fid->i32.gen); + break; + } + + return exportfs_d_alloc(inode); +} +EXPORT_SYMBOL_GPL(generic_fh_to_dentry); + +/** + * generic_fh_to_dentry - generic helper for the fh_to_parent export operation + * @sb: filesystem to do the file handle conversion on + * @fid: file handle to convert + * @fh_len: length of the file handle in bytes + * @fh_type: type of file handle + * @get_inode: filesystem callback to retrieve inode + * + * This function decodes @fid as long as it has one of the well-known + * Linux filehandle types and calls @get_inode on it to retrieve the + * inode for the _parent_ object specified in the file handle if it + * is specified in the file handle, or NULL otherwise. + */ +struct dentry *generic_fh_to_parent(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type, struct inode *(*get_inode) + (struct super_block *sb, u64 ino, u32 gen)) +{ + struct inode *inode = NULL; + + if (fh_len <= 2) + return NULL; + + switch (fh_type) { + case FILEID_INO32_GEN_PARENT: + inode = get_inode(sb, fid->i32.parent_ino, + (fh_len > 3 ? fid->i32.parent_gen : 0)); + break; + } + + return exportfs_d_alloc(inode); +} +EXPORT_SYMBOL_GPL(generic_fh_to_parent); + EXPORT_SYMBOL(dcache_dir_close); EXPORT_SYMBOL(dcache_dir_lseek); EXPORT_SYMBOL(dcache_dir_open); diff --git a/fs/locks.c b/fs/locks.c index 0127a284681..8b8388eca05 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -696,17 +696,28 @@ EXPORT_SYMBOL(posix_test_lock); * Note: the above assumption may not be true when handling lock requests * from a broken NFS client. But broken NFS clients have a lot more to * worry about than proper deadlock detection anyway... --okir + * + * However, the failure of this assumption (also possible in the case of + * multiple tasks sharing the same open file table) also means there's no + * guarantee that the loop below will terminate. As a hack, we give up + * after a few iterations. */ + +#define MAX_DEADLK_ITERATIONS 10 + static int posix_locks_deadlock(struct file_lock *caller_fl, struct file_lock *block_fl) { struct file_lock *fl; + int i = 0; next_task: if (posix_same_owner(caller_fl, block_fl)) return 1; list_for_each_entry(fl, &blocked_list, fl_link) { if (posix_same_owner(fl, block_fl)) { + if (i++ > MAX_DEADLK_ITERATIONS) + return 0; fl = fl->fl_next; block_fl = fl; goto next_task; diff --git a/fs/mbcache.c b/fs/mbcache.c index 1046cbefbfb..eb31b73e7d6 100644 --- a/fs/mbcache.c +++ b/fs/mbcache.c @@ -403,9 +403,9 @@ mb_cache_entry_alloc(struct mb_cache *cache) { struct mb_cache_entry *ce; - atomic_inc(&cache->c_entry_count); ce = kmem_cache_alloc(cache->c_entry_cache, GFP_KERNEL); if (ce) { + atomic_inc(&cache->c_entry_count); INIT_LIST_HEAD(&ce->e_lru_list); INIT_LIST_HEAD(&ce->e_block_list); ce->e_cache = cache; diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 32fe97211ee..afcab007a22 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -549,13 +549,13 @@ static void nfs_direct_write_result(struct rpc_task *task, void *calldata) spin_lock(&dreq->lock); - if (unlikely(dreq->error != 0)) - goto out_unlock; if (unlikely(status < 0)) { - /* An error has occured, so we should not commit */ + /* An error has occurred, so we should not commit */ dreq->flags = 0; dreq->error = status; } + if (unlikely(dreq->error != 0)) + goto out_unlock; dreq->count += data->res.count; diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 04b26672980..66d0aeb32a4 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -386,15 +386,13 @@ static int check_export(struct inode *inode, int flags, unsigned char *uuid) dprintk("exp_export: export of non-dev fs without fsid\n"); return -EINVAL; } - if (!inode->i_sb->s_export_op) { + + if (!inode->i_sb->s_export_op || + !inode->i_sb->s_export_op->fh_to_dentry) { dprintk("exp_export: export of invalid fs type.\n"); return -EINVAL; } - /* Ok, we can export it */; - if (!inode->i_sb->s_export_op->find_exported_dentry) - inode->i_sb->s_export_op->find_exported_dentry = - find_exported_dentry; return 0; } diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index ebd03cc0747..1602cd00dd4 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -43,7 +43,7 @@ #include <linux/file.h> #include <linux/namei.h> #include <asm/uaccess.h> -#include <asm/scatterlist.h> +#include <linux/scatterlist.h> #include <linux/crypto.h> #include <linux/sched.h> @@ -88,7 +88,7 @@ nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname) { struct xdr_netobj cksum; struct hash_desc desc; - struct scatterlist sg[1]; + struct scatterlist sg; __be32 status = nfserr_resource; dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n", @@ -102,11 +102,9 @@ nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname) if (cksum.data == NULL) goto out; - sg[0].page = virt_to_page(clname->data); - sg[0].offset = offset_in_page(clname->data); - sg[0].length = clname->len; + sg_init_one(&sg, clname->data, clname->len); - if (crypto_hash_digest(&desc, sg, sg->length, cksum.data)) + if (crypto_hash_digest(&desc, &sg, sg.length, cksum.data)) goto out; md5_to_hex(dname, cksum.data); diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 7011d62acfc..4f712e97058 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -115,8 +115,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp)); if (!fhp->fh_dentry) { - __u32 *datap=NULL; - __u32 tfh[3]; /* filehandle fragment for oldstyle filehandles */ + struct fid *fid = NULL, sfid; int fileid_type; int data_left = fh->fh_size/4; @@ -128,7 +127,6 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) if (fh->fh_version == 1) { int len; - datap = fh->fh_auth; if (--data_left<0) goto out; switch (fh->fh_auth_type) { case 0: break; @@ -144,9 +142,11 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) fh->fh_fsid[1] = fh->fh_fsid[2]; } if ((data_left -= len)<0) goto out; - exp = rqst_exp_find(rqstp, fh->fh_fsid_type, datap); - datap += len; + exp = rqst_exp_find(rqstp, fh->fh_fsid_type, + fh->fh_auth); + fid = (struct fid *)(fh->fh_auth + len); } else { + __u32 tfh[2]; dev_t xdev; ino_t xino; if (fh->fh_size != NFS_FHSIZE) @@ -190,22 +190,22 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) error = nfserr_badhandle; if (fh->fh_version != 1) { - tfh[0] = fh->ofh_ino; - tfh[1] = fh->ofh_generation; - tfh[2] = fh->ofh_dirino; - datap = tfh; + sfid.i32.ino = fh->ofh_ino; + sfid.i32.gen = fh->ofh_generation; + sfid.i32.parent_ino = fh->ofh_dirino; + fid = &sfid; data_left = 3; if (fh->ofh_dirino == 0) - fileid_type = 1; + fileid_type = FILEID_INO32_GEN; else - fileid_type = 2; + fileid_type = FILEID_INO32_GEN_PARENT; } else fileid_type = fh->fh_fileid_type; - if (fileid_type == 0) + if (fileid_type == FILEID_ROOT) dentry = dget(exp->ex_dentry); else { - dentry = exportfs_decode_fh(exp->ex_mnt, datap, + dentry = exportfs_decode_fh(exp->ex_mnt, fid, data_left, fileid_type, nfsd_acceptable, exp); } @@ -286,16 +286,21 @@ out: * an inode. In this case a call to fh_update should be made * before the fh goes out on the wire ... */ -static inline int _fh_update(struct dentry *dentry, struct svc_export *exp, - __u32 *datap, int *maxsize) +static void _fh_update(struct svc_fh *fhp, struct svc_export *exp, + struct dentry *dentry) { - if (dentry == exp->ex_dentry) { - *maxsize = 0; - return 0; - } + if (dentry != exp->ex_dentry) { + struct fid *fid = (struct fid *) + (fhp->fh_handle.fh_auth + fhp->fh_handle.fh_size/4 - 1); + int maxsize = (fhp->fh_maxsize - fhp->fh_handle.fh_size)/4; + int subtreecheck = !(exp->ex_flags & NFSEXP_NOSUBTREECHECK); - return exportfs_encode_fh(dentry, datap, maxsize, - !(exp->ex_flags & NFSEXP_NOSUBTREECHECK)); + fhp->fh_handle.fh_fileid_type = + exportfs_encode_fh(dentry, fid, &maxsize, subtreecheck); + fhp->fh_handle.fh_size += maxsize * 4; + } else { + fhp->fh_handle.fh_fileid_type = FILEID_ROOT; + } } /* @@ -457,12 +462,8 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, datap += len/4; fhp->fh_handle.fh_size = 4 + len; - if (inode) { - int size = (fhp->fh_maxsize-len-4)/4; - fhp->fh_handle.fh_fileid_type = - _fh_update(dentry, exp, datap, &size); - fhp->fh_handle.fh_size += size*4; - } + if (inode) + _fh_update(fhp, exp, dentry); if (fhp->fh_handle.fh_fileid_type == 255) return nfserr_opnotsupp; } @@ -479,7 +480,6 @@ __be32 fh_update(struct svc_fh *fhp) { struct dentry *dentry; - __u32 *datap; if (!fhp->fh_dentry) goto out_bad; @@ -490,15 +490,10 @@ fh_update(struct svc_fh *fhp) if (fhp->fh_handle.fh_version != 1) { _fh_update_old(dentry, fhp->fh_export, &fhp->fh_handle); } else { - int size; - if (fhp->fh_handle.fh_fileid_type != 0) + if (fhp->fh_handle.fh_fileid_type != FILEID_ROOT) goto out; - datap = fhp->fh_handle.fh_auth+ - fhp->fh_handle.fh_size/4 -1; - size = (fhp->fh_maxsize - fhp->fh_handle.fh_size)/4; - fhp->fh_handle.fh_fileid_type = - _fh_update(dentry, fhp->fh_export, datap, &size); - fhp->fh_handle.fh_size += size*4; + + _fh_update(fhp, fhp->fh_export, dentry); if (fhp->fh_handle.fh_fileid_type == 255) return nfserr_opnotsupp; } diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index cfdc7900d27..ad87cb01299 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -405,6 +405,15 @@ static int ntfs_readpage(struct file *file, struct page *page) retry_readpage: BUG_ON(!PageLocked(page)); + vi = page->mapping->host; + i_size = i_size_read(vi); + /* Is the page fully outside i_size? (truncate in progress) */ + if (unlikely(page->index >= (i_size + PAGE_CACHE_SIZE - 1) >> + PAGE_CACHE_SHIFT)) { + zero_user_page(page, 0, PAGE_CACHE_SIZE, KM_USER0); + ntfs_debug("Read outside i_size - truncated?"); + goto done; + } /* * This can potentially happen because we clear PageUptodate() during * ntfs_writepage() of MstProtected() attributes. @@ -413,7 +422,6 @@ retry_readpage: unlock_page(page); return 0; } - vi = page->mapping->host; ni = NTFS_I(vi); /* * Only $DATA attributes can be encrypted and only unnamed $DATA diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 92dabdcf2b8..50d3b0c258e 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -179,10 +179,7 @@ int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn, ntfs_attr_search_ctx *ctx) * ntfs_mapping_pairs_decompress() fails. */ end_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn) + 1; - if (!a->data.non_resident.lowest_vcn && end_vcn == 1) - end_vcn = sle64_to_cpu(a->data.non_resident.allocated_size) >> - ni->vol->cluster_size_bits; - if (unlikely(vcn >= end_vcn)) { + if (unlikely(vcn && vcn >= end_vcn)) { err = -ENOENT; goto err_out; } diff --git a/fs/ntfs/compress.c b/fs/ntfs/compress.c index d98daf59e0b..d1619d05eb2 100644 --- a/fs/ntfs/compress.c +++ b/fs/ntfs/compress.c @@ -561,6 +561,16 @@ int ntfs_read_compressed_block(struct page *page) read_unlock_irqrestore(&ni->size_lock, flags); max_page = ((i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT) - offset; + /* Is the page fully outside i_size? (truncate in progress) */ + if (xpage >= max_page) { + kfree(bhs); + kfree(pages); + zero_user_page(page, 0, PAGE_CACHE_SIZE, KM_USER0); + ntfs_debug("Compressed read outside i_size - truncated?"); + SetPageUptodate(page); + unlock_page(page); + return 0; + } if (nr_pages < max_page) max_page = nr_pages; for (i = 0; i < max_page; i++, offset++) { diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c index e93c6142b23..e1781c8b165 100644 --- a/fs/ntfs/namei.c +++ b/fs/ntfs/namei.c @@ -450,58 +450,40 @@ try_next: return parent_dent; } -/** - * ntfs_get_dentry - find a dentry for the inode from a file handle sub-fragment - * @sb: super block identifying the mounted ntfs volume - * @fh: the file handle sub-fragment - * - * Find a dentry for the inode given a file handle sub-fragment. This function - * is called from fs/exportfs/expfs.c::find_exported_dentry() which in turn is - * called from the default ->decode_fh() which is export_decode_fh() in the - * same file. The code is closely based on the default ->get_dentry() helper - * fs/exportfs/expfs.c::get_object(). - * - * The @fh contains two 32-bit unsigned values, the first one is the inode - * number and the second one is the inode generation. - * - * Return the dentry on success or the error code on error (IS_ERR() is true). - */ -static struct dentry *ntfs_get_dentry(struct super_block *sb, void *fh) +static struct inode *ntfs_nfs_get_inode(struct super_block *sb, + u64 ino, u32 generation) { - struct inode *vi; - struct dentry *dent; - unsigned long ino = ((u32 *)fh)[0]; - u32 gen = ((u32 *)fh)[1]; + struct inode *inode; - ntfs_debug("Entering for inode 0x%lx, generation 0x%x.", ino, gen); - vi = ntfs_iget(sb, ino); - if (IS_ERR(vi)) { - ntfs_error(sb, "Failed to get inode 0x%lx.", ino); - return (struct dentry *)vi; - } - if (unlikely(is_bad_inode(vi) || vi->i_generation != gen)) { - /* We didn't find the right inode. */ - ntfs_error(sb, "Inode 0x%lx, bad count: %d %d or version 0x%x " - "0x%x.", vi->i_ino, vi->i_nlink, - atomic_read(&vi->i_count), vi->i_generation, - gen); - iput(vi); - return ERR_PTR(-ESTALE); - } - /* Now find a dentry. If possible, get a well-connected one. */ - dent = d_alloc_anon(vi); - if (unlikely(!dent)) { - iput(vi); - return ERR_PTR(-ENOMEM); + inode = ntfs_iget(sb, ino); + if (!IS_ERR(inode)) { + if (is_bad_inode(inode) || inode->i_generation != generation) { + iput(inode); + inode = ERR_PTR(-ESTALE); + } } - ntfs_debug("Done for inode 0x%lx, generation 0x%x.", ino, gen); - return dent; + + return inode; +} + +static struct dentry *ntfs_fh_to_dentry(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_dentry(sb, fid, fh_len, fh_type, + ntfs_nfs_get_inode); +} + +static struct dentry *ntfs_fh_to_parent(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_parent(sb, fid, fh_len, fh_type, + ntfs_nfs_get_inode); } /** * Export operations allowing NFS exporting of mounted NTFS partitions. * - * We use the default ->decode_fh() and ->encode_fh() for now. Note that they + * We use the default ->encode_fh() for now. Note that they * use 32 bits to store the inode number which is an unsigned long so on 64-bit * architectures is usually 64 bits so it would all fail horribly on huge * volumes. I guess we need to define our own encode and decode fh functions @@ -517,10 +499,9 @@ static struct dentry *ntfs_get_dentry(struct super_block *sb, void *fh) * allowing the inode number 0 which is used in NTFS for the system file $MFT * and due to using iget() whereas NTFS needs ntfs_iget(). */ -struct export_operations ntfs_export_ops = { +const struct export_operations ntfs_export_ops = { .get_parent = ntfs_get_parent, /* Find the parent of a given directory. */ - .get_dentry = ntfs_get_dentry, /* Find a dentry for the inode - given a file handle - sub-fragment. */ + .fh_to_dentry = ntfs_fh_to_dentry, + .fh_to_parent = ntfs_fh_to_parent, }; diff --git a/fs/ntfs/ntfs.h b/fs/ntfs/ntfs.h index d73f5a9ac34..d6a340bf80f 100644 --- a/fs/ntfs/ntfs.h +++ b/fs/ntfs/ntfs.h @@ -69,7 +69,7 @@ extern const struct inode_operations ntfs_dir_inode_ops; extern const struct file_operations ntfs_empty_file_ops; extern const struct inode_operations ntfs_empty_inode_ops; -extern struct export_operations ntfs_export_ops; +extern const struct export_operations ntfs_export_ops; /** * NTFS_SB - return the ntfs volume given a vfs super block diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index 4ba7f0bdc24..ce62c152823 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -3946,7 +3946,7 @@ static int __ocfs2_mark_extent_written(struct inode *inode, struct ocfs2_merge_ctxt ctxt; struct ocfs2_extent_list *rightmost_el; - if (!rec->e_flags & OCFS2_EXT_UNWRITTEN) { + if (!(rec->e_flags & OCFS2_EXT_UNWRITTEN)) { ret = -EIO; mlog_errno(ret); goto out; diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index c69c1b30015..556e34ccb00 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -729,6 +729,27 @@ static void ocfs2_clear_page_regions(struct page *page, } /* + * Nonsparse file systems fully allocate before we get to the write + * code. This prevents ocfs2_write() from tagging the write as an + * allocating one, which means ocfs2_map_page_blocks() might try to + * read-in the blocks at the tail of our file. Avoid reading them by + * testing i_size against each block offset. + */ +static int ocfs2_should_read_blk(struct inode *inode, struct page *page, + unsigned int block_start) +{ + u64 offset = page_offset(page) + block_start; + + if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) + return 1; + + if (i_size_read(inode) > offset) + return 1; + + return 0; +} + +/* * Some of this taken from block_prepare_write(). We already have our * mapping by now though, and the entire write will be allocating or * it won't, so not much need to use BH_New. @@ -781,6 +802,7 @@ int ocfs2_map_page_blocks(struct page *page, u64 *p_blkno, set_buffer_uptodate(bh); } else if (!buffer_uptodate(bh) && !buffer_delay(bh) && !buffer_new(bh) && + ocfs2_should_read_blk(inode, page, block_start) && (block_start < from || block_end > to)) { ll_rw_block(READ, 1, &bh); *wait_bh++=bh; diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 9cc7c0418b7..f02ccb34604 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -267,7 +267,7 @@ static struct bio *o2hb_setup_one_bio(struct o2hb_region *reg, current_page = cs / spp; page = reg->hr_slot_data[current_page]; - vec_len = min(PAGE_CACHE_SIZE, + vec_len = min(PAGE_CACHE_SIZE - vec_start, (max_slots-cs) * (PAGE_CACHE_SIZE/spp) ); mlog(ML_HB_BIO, "page %d, vec_len = %u, vec_start = %u\n", diff --git a/fs/ocfs2/dcache.c b/fs/ocfs2/dcache.c index 3094ddb7a25..1957a5ed219 100644 --- a/fs/ocfs2/dcache.c +++ b/fs/ocfs2/dcache.c @@ -318,9 +318,9 @@ out_attach: static void ocfs2_drop_dentry_lock(struct ocfs2_super *osb, struct ocfs2_dentry_lock *dl) { + iput(dl->dl_inode); ocfs2_simple_drop_lockres(osb, &dl->dl_lockres); ocfs2_lock_res_free(&dl->dl_lockres); - iput(dl->dl_inode); kfree(dl); } diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index 6a2f143e269..63b28fdceb4 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -208,9 +208,9 @@ out: return NULL; } -struct buffer_head *ocfs2_find_entry_el(const char *name, int namelen, - struct inode *dir, - struct ocfs2_dir_entry **res_dir) +static struct buffer_head *ocfs2_find_entry_el(const char *name, int namelen, + struct inode *dir, + struct ocfs2_dir_entry **res_dir) { struct super_block *sb; struct buffer_head *bh_use[NAMEI_RA_SIZE]; diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index 41c76ff2fcf..4e97dcceaf8 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -670,7 +670,7 @@ static inline void ocfs2_generic_handle_attach_action(struct ocfs2_lock_res *loc { mlog_entry_void(); - BUG_ON((!lockres->l_flags & OCFS2_LOCK_BUSY)); + BUG_ON((!(lockres->l_flags & OCFS2_LOCK_BUSY))); BUG_ON(lockres->l_flags & OCFS2_LOCK_ATTACHED); if (lockres->l_requested > LKM_NLMODE && @@ -980,18 +980,6 @@ again: goto unlock; } - if (!(lockres->l_flags & OCFS2_LOCK_ATTACHED)) { - /* lock has not been created yet. */ - spin_unlock_irqrestore(&lockres->l_lock, flags); - - ret = ocfs2_lock_create(osb, lockres, LKM_NLMODE, 0); - if (ret < 0) { - mlog_errno(ret); - goto out; - } - goto again; - } - if (lockres->l_flags & OCFS2_LOCK_BLOCKED && !ocfs2_may_continue_on_blocked_lock(lockres, level)) { /* is the lock is currently blocked on behalf of @@ -1006,7 +994,14 @@ again: mlog(ML_ERROR, "lockres %s has action %u pending\n", lockres->l_name, lockres->l_action); - lockres->l_action = OCFS2_AST_CONVERT; + if (!(lockres->l_flags & OCFS2_LOCK_ATTACHED)) { + lockres->l_action = OCFS2_AST_ATTACH; + lkm_flags &= ~LKM_CONVERT; + } else { + lockres->l_action = OCFS2_AST_CONVERT; + lkm_flags |= LKM_CONVERT; + } + lockres->l_requested = level; lockres_or_flags(lockres, OCFS2_LOCK_BUSY); spin_unlock_irqrestore(&lockres->l_lock, flags); @@ -1021,7 +1016,7 @@ again: status = dlmlock(osb->dlm, level, &lockres->l_lksb, - lkm_flags|LKM_CONVERT, + lkm_flags, lockres->l_name, OCFS2_LOCK_ID_MAX_LEN - 1, ocfs2_locking_ast, diff --git a/fs/ocfs2/export.c b/fs/ocfs2/export.c index c3bbc198f9c..535bfa9568a 100644 --- a/fs/ocfs2/export.c +++ b/fs/ocfs2/export.c @@ -45,9 +45,9 @@ struct ocfs2_inode_handle u32 ih_generation; }; -static struct dentry *ocfs2_get_dentry(struct super_block *sb, void *vobjp) +static struct dentry *ocfs2_get_dentry(struct super_block *sb, + struct ocfs2_inode_handle *handle) { - struct ocfs2_inode_handle *handle = vobjp; struct inode *inode; struct dentry *result; @@ -194,54 +194,37 @@ bail: return type; } -static struct dentry *ocfs2_decode_fh(struct super_block *sb, u32 *fh_in, - int fh_len, int fileid_type, - int (*acceptable)(void *context, - struct dentry *de), - void *context) +static struct dentry *ocfs2_fh_to_dentry(struct super_block *sb, + struct fid *fid, int fh_len, int fh_type) { - struct ocfs2_inode_handle handle, parent; - struct dentry *ret = NULL; - __le32 *fh = (__force __le32 *) fh_in; - - mlog_entry("(0x%p, 0x%p, %d, %d, 0x%p, 0x%p)\n", - sb, fh, fh_len, fileid_type, acceptable, context); - - if (fh_len < 3 || fileid_type > 2) - goto bail; - - if (fileid_type == 2) { - if (fh_len < 6) - goto bail; - - parent.ih_blkno = (u64)le32_to_cpu(fh[3]) << 32; - parent.ih_blkno |= (u64)le32_to_cpu(fh[4]); - parent.ih_generation = le32_to_cpu(fh[5]); + struct ocfs2_inode_handle handle; - mlog(0, "Decoding parent: blkno: %llu, generation: %u\n", - (unsigned long long)parent.ih_blkno, - parent.ih_generation); - } + if (fh_len < 3 || fh_type > 2) + return NULL; - handle.ih_blkno = (u64)le32_to_cpu(fh[0]) << 32; - handle.ih_blkno |= (u64)le32_to_cpu(fh[1]); - handle.ih_generation = le32_to_cpu(fh[2]); + handle.ih_blkno = (u64)le32_to_cpu(fid->raw[0]) << 32; + handle.ih_blkno |= (u64)le32_to_cpu(fid->raw[1]); + handle.ih_generation = le32_to_cpu(fid->raw[2]); + return ocfs2_get_dentry(sb, &handle); +} - mlog(0, "Encoding fh: blkno: %llu, generation: %u\n", - (unsigned long long)handle.ih_blkno, handle.ih_generation); +static struct dentry *ocfs2_fh_to_parent(struct super_block *sb, + struct fid *fid, int fh_len, int fh_type) +{ + struct ocfs2_inode_handle parent; - ret = ocfs2_export_ops.find_exported_dentry(sb, &handle, &parent, - acceptable, context); + if (fh_type != 2 || fh_len < 6) + return NULL; -bail: - mlog_exit_ptr(ret); - return ret; + parent.ih_blkno = (u64)le32_to_cpu(fid->raw[3]) << 32; + parent.ih_blkno |= (u64)le32_to_cpu(fid->raw[4]); + parent.ih_generation = le32_to_cpu(fid->raw[5]); + return ocfs2_get_dentry(sb, &parent); } -struct export_operations ocfs2_export_ops = { - .decode_fh = ocfs2_decode_fh, +const struct export_operations ocfs2_export_ops = { .encode_fh = ocfs2_encode_fh, - + .fh_to_dentry = ocfs2_fh_to_dentry, + .fh_to_parent = ocfs2_fh_to_parent, .get_parent = ocfs2_get_parent, - .get_dentry = ocfs2_get_dentry, }; diff --git a/fs/ocfs2/export.h b/fs/ocfs2/export.h index e08bed9e45a..41a738678c3 100644 --- a/fs/ocfs2/export.h +++ b/fs/ocfs2/export.h @@ -28,6 +28,6 @@ #include <linux/exportfs.h> -extern struct export_operations ocfs2_export_ops; +extern const struct export_operations ocfs2_export_ops; #endif /* OCFS2_EXPORT_H */ diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index f92fe91ff26..bbac7cd33e0 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -1891,9 +1891,11 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb, ssize_t written = 0; size_t ocount; /* original count */ size_t count; /* after file limit checks */ - loff_t *ppos = &iocb->ki_pos; + loff_t old_size, *ppos = &iocb->ki_pos; + u32 old_clusters; struct file *file = iocb->ki_filp; struct inode *inode = file->f_path.dentry->d_inode; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); mlog_entry("(0x%p, %u, '%.*s')\n", file, (unsigned int)nr_segs, @@ -1949,6 +1951,13 @@ relock: goto relock; } + /* + * To later detect whether a journal commit for sync writes is + * necessary, we sample i_size, and cluster count here. + */ + old_size = i_size_read(inode); + old_clusters = OCFS2_I(inode)->ip_clusters; + /* communicate with ocfs2_dio_end_io */ ocfs2_iocb_set_rw_locked(iocb, rw_level); @@ -1978,6 +1987,21 @@ out_dio: /* buffered aio wouldn't have proper lock coverage today */ BUG_ON(ret == -EIOCBQUEUED && !(file->f_flags & O_DIRECT)); + if ((file->f_flags & O_SYNC && !direct_io) || IS_SYNC(inode)) { + /* + * The generic write paths have handled getting data + * to disk, but since we don't make use of the dirty + * inode list, a manual journal commit is necessary + * here. + */ + if (old_size != i_size_read(inode) || + old_clusters != OCFS2_I(inode)->ip_clusters) { + ret = journal_force_commit(osb->journal->j_journal); + if (ret < 0) + written = ret; + } + } + /* * deep in g_f_a_w_n()->ocfs2_direct_IO we pass in a ocfs2_dio_end_io * function pointer which is called when o_direct io completes so that diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 729259016c1..989ac271858 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -1105,9 +1105,16 @@ static int ocfs2_rename(struct inode *old_dir, goto bail; } - if (!new_de && new_inode) - mlog(ML_ERROR, "inode %lu does not exist in it's parent " - "directory!", new_inode->i_ino); + if (!new_de && new_inode) { + /* + * Target was unlinked by another node while we were + * waiting to get to ocfs2_rename(). There isn't + * anything we can do here to help the situation, so + * bubble up the appropriate error. + */ + status = -ENOENT; + goto bail; + } /* In case we need to overwrite an existing file, we blow it * away first */ diff --git a/fs/proc/array.c b/fs/proc/array.c index 63c95afb561..eba339ecba2 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -358,7 +358,8 @@ static cputime_t task_utime(struct task_struct *p) } utime = (clock_t)temp; - return clock_t_to_cputime(utime); + p->prev_utime = max(p->prev_utime, clock_t_to_cputime(utime)); + return p->prev_utime; } static cputime_t task_stime(struct task_struct *p) @@ -373,7 +374,8 @@ static cputime_t task_stime(struct task_struct *p) stime = nsec_to_clock_t(p->se.sum_exec_runtime) - cputime_to_clock_t(task_utime(p)); - return clock_t_to_cputime(stime); + p->prev_stime = max(p->prev_stime, clock_t_to_cputime(stime)); + return p->prev_stime; } #endif diff --git a/fs/proc/base.c b/fs/proc/base.c index 39a3d7c969c..aeaf0d0f2f5 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2255,27 +2255,6 @@ static const struct inode_operations proc_tgid_base_inode_operations = { .setattr = proc_setattr, }; -/** - * proc_flush_task - Remove dcache entries for @task from the /proc dcache. - * - * @task: task that should be flushed. - * - * Looks in the dcache for - * /proc/@pid - * /proc/@tgid/task/@pid - * if either directory is present flushes it and all of it'ts children - * from the dcache. - * - * It is safe and reasonable to cache /proc entries for a task until - * that task exits. After that they just clog up the dcache with - * useless entries, possibly causing useful dcache entries to be - * flushed instead. This routine is proved to flush those useless - * dcache entries at process exit time. - * - * NOTE: This routine is just an optimization so it does not guarantee - * that no dcache entries will exist at process exit time it - * just makes it very unlikely that any will persist. - */ static void proc_flush_task_mnt(struct vfsmount *mnt, pid_t pid, pid_t tgid) { struct dentry *dentry, *leader, *dir; @@ -2322,10 +2301,29 @@ out: return; } -/* - * when flushing dentries from proc one need to flush them from global +/** + * proc_flush_task - Remove dcache entries for @task from the /proc dcache. + * @task: task that should be flushed. + * + * When flushing dentries from proc, one needs to flush them from global * proc (proc_mnt) and from all the namespaces' procs this task was seen - * in. this call is supposed to make all this job. + * in. This call is supposed to do all of this job. + * + * Looks in the dcache for + * /proc/@pid + * /proc/@tgid/task/@pid + * if either directory is present flushes it and all of it'ts children + * from the dcache. + * + * It is safe and reasonable to cache /proc entries for a task until + * that task exits. After that they just clog up the dcache with + * useless entries, possibly causing useful dcache entries to be + * flushed instead. This routine is proved to flush those useless + * dcache entries at process exit time. + * + * NOTE: This routine is just an optimization so it does not guarantee + * that no dcache entries will exist at process exit time it + * just makes it very unlikely that any will persist. */ void proc_flush_task(struct task_struct *task) diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index 2e91fb756e9..153554cf557 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c @@ -26,13 +26,6 @@ #include "internal.h" -struct proc_dir_entry *proc_net_create(struct net *net, - const char *name, mode_t mode, get_info_t *get_info) -{ - return create_proc_info_entry(name,mode, net->proc_net, get_info); -} -EXPORT_SYMBOL_GPL(proc_net_create); - struct proc_dir_entry *proc_net_fops_create(struct net *net, const char *name, mode_t mode, const struct file_operations *fops) { @@ -185,7 +178,7 @@ static __net_exit void proc_net_ns_exit(struct net *net) kfree(net->proc_net_root); } -struct pernet_operations __net_initdata proc_net_ns_ops = { +static struct pernet_operations proc_net_ns_ops = { .init = proc_net_ns_init, .exit = proc_net_ns_exit, }; diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 680c429bfa2..4e57fcf8598 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -171,7 +171,8 @@ static ssize_t proc_sys_read(struct file *filp, char __user *buf, struct dentry *dentry = filp->f_dentry; struct ctl_table_header *head; struct ctl_table *table; - ssize_t error, res; + ssize_t error; + size_t res; table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head); /* Has the sysctl entry disappeared on us? */ @@ -209,7 +210,8 @@ static ssize_t proc_sys_write(struct file *filp, const char __user *buf, struct dentry *dentry = filp->f_dentry; struct ctl_table_header *head; struct ctl_table *table; - ssize_t error, res; + ssize_t error; + size_t res; table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head); /* Has the sysctl entry disappeared on us? */ diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index a991af96f3f..231fd5ccadc 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -1515,19 +1515,20 @@ struct inode *reiserfs_iget(struct super_block *s, const struct cpu_key *key) return inode; } -struct dentry *reiserfs_get_dentry(struct super_block *sb, void *vobjp) +static struct dentry *reiserfs_get_dentry(struct super_block *sb, + u32 objectid, u32 dir_id, u32 generation) + { - __u32 *data = vobjp; struct cpu_key key; struct dentry *result; struct inode *inode; - key.on_disk_key.k_objectid = data[0]; - key.on_disk_key.k_dir_id = data[1]; + key.on_disk_key.k_objectid = objectid; + key.on_disk_key.k_dir_id = dir_id; reiserfs_write_lock(sb); inode = reiserfs_iget(sb, &key); - if (inode && !IS_ERR(inode) && data[2] != 0 && - data[2] != inode->i_generation) { + if (inode && !IS_ERR(inode) && generation != 0 && + generation != inode->i_generation) { iput(inode); inode = NULL; } @@ -1544,14 +1545,9 @@ struct dentry *reiserfs_get_dentry(struct super_block *sb, void *vobjp) return result; } -struct dentry *reiserfs_decode_fh(struct super_block *sb, __u32 * data, - int len, int fhtype, - int (*acceptable) (void *contect, - struct dentry * de), - void *context) +struct dentry *reiserfs_fh_to_dentry(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) { - __u32 obj[3], parent[3]; - /* fhtype happens to reflect the number of u32s encoded. * due to a bug in earlier code, fhtype might indicate there * are more u32s then actually fitted. @@ -1564,32 +1560,28 @@ struct dentry *reiserfs_decode_fh(struct super_block *sb, __u32 * data, * 6 - as above plus generation of directory * 6 does not fit in NFSv2 handles */ - if (fhtype > len) { - if (fhtype != 6 || len != 5) + if (fh_type > fh_len) { + if (fh_type != 6 || fh_len != 5) reiserfs_warning(sb, - "nfsd/reiserfs, fhtype=%d, len=%d - odd", - fhtype, len); - fhtype = 5; + "nfsd/reiserfs, fhtype=%d, len=%d - odd", + fh_type, fh_len); + fh_type = 5; } - obj[0] = data[0]; - obj[1] = data[1]; - if (fhtype == 3 || fhtype >= 5) - obj[2] = data[2]; - else - obj[2] = 0; /* generation number */ + return reiserfs_get_dentry(sb, fid->raw[0], fid->raw[1], + (fh_type == 3 || fh_type >= 5) ? fid->raw[2] : 0); +} - if (fhtype >= 4) { - parent[0] = data[fhtype >= 5 ? 3 : 2]; - parent[1] = data[fhtype >= 5 ? 4 : 3]; - if (fhtype == 6) - parent[2] = data[5]; - else - parent[2] = 0; - } - return sb->s_export_op->find_exported_dentry(sb, obj, - fhtype < 4 ? NULL : parent, - acceptable, context); +struct dentry *reiserfs_fh_to_parent(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + if (fh_type < 4) + return NULL; + + return reiserfs_get_dentry(sb, + (fh_type >= 5) ? fid->raw[3] : fid->raw[2], + (fh_type >= 5) ? fid->raw[4] : fid->raw[3], + (fh_type == 6) ? fid->raw[5] : 0); } int reiserfs_encode_fh(struct dentry *dentry, __u32 * data, int *lenp, diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index 98c3781bc06..5cd85fe5df5 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -661,11 +661,11 @@ static struct quotactl_ops reiserfs_qctl_operations = { }; #endif -static struct export_operations reiserfs_export_ops = { +static const struct export_operations reiserfs_export_ops = { .encode_fh = reiserfs_encode_fh, - .decode_fh = reiserfs_decode_fh, + .fh_to_dentry = reiserfs_fh_to_dentry, + .fh_to_parent = reiserfs_fh_to_parent, .get_parent = reiserfs_get_parent, - .get_dentry = reiserfs_get_dentry, }; /* this struct is used in reiserfs_getopt () for containing the value for those diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 7a8ce9e98b3..337162935d2 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -132,7 +132,7 @@ struct dentry *sysfs_get_dentry(struct sysfs_dirent *sd) * RETURNS: * Pointer to @sd on success, NULL on failure. */ -struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) +static struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) { if (unlikely(!sd)) return NULL; @@ -161,7 +161,7 @@ struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) * Put an active reference to @sd. This function is noop if @sd * is NULL. */ -void sysfs_put_active(struct sysfs_dirent *sd) +static void sysfs_put_active(struct sysfs_dirent *sd) { struct completion *cmpl; int v; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index f8417988f6b..ff17f8da9b4 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -103,8 +103,6 @@ extern const struct file_operations sysfs_dir_operations; extern const struct inode_operations sysfs_dir_inode_operations; struct dentry *sysfs_get_dentry(struct sysfs_dirent *sd); -struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd); -void sysfs_put_active(struct sysfs_dirent *sd); struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd); void sysfs_put_active_two(struct sysfs_dirent *sd); void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt, diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 584cf12cc40..c78c04fd993 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -933,20 +933,19 @@ magic_found: goto again; } - /* Set sbi->s_flags here, used by ufs_get_fs_state() below */ - sbi->s_flags = flags; + sbi->s_flags = flags;/*after that line some functions use s_flags*/ ufs_print_super_stuff(sb, usb1, usb2, usb3); /* * Check, if file system was correctly unmounted. * If not, make it read only. */ - if ((((flags & UFS_ST_MASK) == UFS_ST_44BSD) || - ((flags & UFS_ST_MASK) == UFS_ST_OLD) || - ((flags & UFS_ST_MASK) == UFS_ST_SUN) || - ((flags & UFS_ST_MASK) == UFS_ST_SUNOS) || - ((flags & UFS_ST_MASK) == UFS_ST_SUNx86)) && - (ufs_get_fs_state(sb, usb1, usb3) == (UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time)))) { + if (((flags & UFS_ST_MASK) == UFS_ST_44BSD) || + ((flags & UFS_ST_MASK) == UFS_ST_OLD) || + (((flags & UFS_ST_MASK) == UFS_ST_SUN || + (flags & UFS_ST_MASK) == UFS_ST_SUNOS || + (flags & UFS_ST_MASK) == UFS_ST_SUNx86) && + (ufs_get_fs_state(sb, usb1, usb3) == (UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time))))) { switch(usb1->fs_clean) { case UFS_FSCLEAN: UFSD("fs is clean\n"); diff --git a/fs/xfs/linux-2.6/xfs_export.c b/fs/xfs/linux-2.6/xfs_export.c index 3586c7a28d2..15bd4948832 100644 --- a/fs/xfs/linux-2.6/xfs_export.c +++ b/fs/xfs/linux-2.6/xfs_export.c @@ -33,62 +33,25 @@ static struct dentry dotdot = { .d_name.name = "..", .d_name.len = 2, }; /* - * XFS encodes and decodes the fileid portion of NFS filehandles - * itself instead of letting the generic NFS code do it. This - * allows filesystems with 64 bit inode numbers to be exported. - * - * Note that a side effect is that xfs_vget() won't be passed a - * zero inode/generation pair under normal circumstances. As - * however a malicious client could send us such data, the check - * remains in that code. + * Note that we only accept fileids which are long enough rather than allow + * the parent generation number to default to zero. XFS considers zero a + * valid generation number not an invalid/wildcard value. */ - -STATIC struct dentry * -xfs_fs_decode_fh( - struct super_block *sb, - __u32 *fh, - int fh_len, - int fileid_type, - int (*acceptable)( - void *context, - struct dentry *de), - void *context) +static int xfs_fileid_length(int fileid_type) { - xfs_fid_t ifid; - xfs_fid_t pfid; - void *parent = NULL; - int is64 = 0; - __u32 *p = fh; - -#if XFS_BIG_INUMS - is64 = (fileid_type & XFS_FILEID_TYPE_64FLAG); - fileid_type &= ~XFS_FILEID_TYPE_64FLAG; -#endif - - /* - * Note that we only accept fileids which are long enough - * rather than allow the parent generation number to default - * to zero. XFS considers zero a valid generation number not - * an invalid/wildcard value. There's little point printk'ing - * a warning here as we don't have the client information - * which would make such a warning useful. - */ - if (fileid_type > 2 || - fh_len < xfs_fileid_length((fileid_type == 2), is64)) - return NULL; - - p = xfs_fileid_decode_fid2(p, &ifid, is64); - - if (fileid_type == 2) { - p = xfs_fileid_decode_fid2(p, &pfid, is64); - parent = &pfid; + switch (fileid_type) { + case FILEID_INO32_GEN: + return 2; + case FILEID_INO32_GEN_PARENT: + return 4; + case FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG: + return 3; + case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG: + return 6; } - - fh = (__u32 *)&ifid; - return sb->s_export_op->find_exported_dentry(sb, fh, parent, acceptable, context); + return 255; /* invalid */ } - STATIC int xfs_fs_encode_fh( struct dentry *dentry, @@ -96,21 +59,21 @@ xfs_fs_encode_fh( int *max_len, int connectable) { + struct fid *fid = (struct fid *)fh; + struct xfs_fid64 *fid64 = (struct xfs_fid64 *)fh; struct inode *inode = dentry->d_inode; - int type = 1; - __u32 *p = fh; + int fileid_type; int len; - int is64 = 0; -#if XFS_BIG_INUMS - if (!(XFS_M(inode->i_sb)->m_flags & XFS_MOUNT_SMALL_INUMS)) { - /* filesystem may contain 64bit inode numbers */ - is64 = XFS_FILEID_TYPE_64FLAG; - } -#endif /* Directories don't need their parent encoded, they have ".." */ if (S_ISDIR(inode->i_mode)) - connectable = 0; + fileid_type = FILEID_INO32_GEN; + else + fileid_type = FILEID_INO32_GEN_PARENT; + + /* filesystem may contain 64bit inode numbers */ + if (!(XFS_M(inode->i_sb)->m_flags & XFS_MOUNT_SMALL_INUMS)) + fileid_type |= XFS_FILEID_TYPE_64FLAG; /* * Only encode if there is enough space given. In practice @@ -118,39 +81,118 @@ xfs_fs_encode_fh( * over NFSv2 with the subtree_check export option; the other * seven combinations work. The real answer is "don't use v2". */ - len = xfs_fileid_length(connectable, is64); + len = xfs_fileid_length(fileid_type); if (*max_len < len) return 255; *max_len = len; - p = xfs_fileid_encode_inode(p, inode, is64); - if (connectable) { + switch (fileid_type) { + case FILEID_INO32_GEN_PARENT: spin_lock(&dentry->d_lock); - p = xfs_fileid_encode_inode(p, dentry->d_parent->d_inode, is64); + fid->i32.parent_ino = dentry->d_parent->d_inode->i_ino; + fid->i32.parent_gen = dentry->d_parent->d_inode->i_generation; spin_unlock(&dentry->d_lock); - type = 2; + /*FALLTHRU*/ + case FILEID_INO32_GEN: + fid->i32.ino = inode->i_ino; + fid->i32.gen = inode->i_generation; + break; + case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG: + spin_lock(&dentry->d_lock); + fid64->parent_ino = dentry->d_parent->d_inode->i_ino; + fid64->parent_gen = dentry->d_parent->d_inode->i_generation; + spin_unlock(&dentry->d_lock); + /*FALLTHRU*/ + case FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG: + fid64->ino = inode->i_ino; + fid64->gen = inode->i_generation; + break; } - BUG_ON((p - fh) != len); - return type | is64; + + return fileid_type; } -STATIC struct dentry * -xfs_fs_get_dentry( +STATIC struct inode * +xfs_nfs_get_inode( struct super_block *sb, - void *data) -{ + u64 ino, + u32 generation) + { + xfs_fid_t xfid; bhv_vnode_t *vp; - struct inode *inode; - struct dentry *result; int error; - error = xfs_vget(XFS_M(sb), &vp, data); - if (error || vp == NULL) - return ERR_PTR(-ESTALE) ; + xfid.fid_len = sizeof(xfs_fid_t) - sizeof(xfid.fid_len); + xfid.fid_pad = 0; + xfid.fid_ino = ino; + xfid.fid_gen = generation; - inode = vn_to_inode(vp); + error = xfs_vget(XFS_M(sb), &vp, &xfid); + if (error) + return ERR_PTR(-error); + + return vp ? vn_to_inode(vp) : NULL; +} + +STATIC struct dentry * +xfs_fs_fh_to_dentry(struct super_block *sb, struct fid *fid, + int fh_len, int fileid_type) +{ + struct xfs_fid64 *fid64 = (struct xfs_fid64 *)fid; + struct inode *inode = NULL; + struct dentry *result; + + if (fh_len < xfs_fileid_length(fileid_type)) + return NULL; + + switch (fileid_type) { + case FILEID_INO32_GEN_PARENT: + case FILEID_INO32_GEN: + inode = xfs_nfs_get_inode(sb, fid->i32.ino, fid->i32.gen); + break; + case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG: + case FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG: + inode = xfs_nfs_get_inode(sb, fid64->ino, fid64->gen); + break; + } + + if (!inode) + return NULL; + if (IS_ERR(inode)) + return ERR_PTR(PTR_ERR(inode)); + result = d_alloc_anon(inode); + if (!result) { + iput(inode); + return ERR_PTR(-ENOMEM); + } + return result; +} + +STATIC struct dentry * +xfs_fs_fh_to_parent(struct super_block *sb, struct fid *fid, + int fh_len, int fileid_type) +{ + struct xfs_fid64 *fid64 = (struct xfs_fid64 *)fid; + struct inode *inode = NULL; + struct dentry *result; + + switch (fileid_type) { + case FILEID_INO32_GEN_PARENT: + inode = xfs_nfs_get_inode(sb, fid->i32.parent_ino, + fid->i32.parent_gen); + break; + case FILEID_INO32_GEN_PARENT | XFS_FILEID_TYPE_64FLAG: + inode = xfs_nfs_get_inode(sb, fid64->parent_ino, + fid64->parent_gen); + break; + } + + if (!inode) + return NULL; + if (IS_ERR(inode)) + return ERR_PTR(PTR_ERR(inode)); result = d_alloc_anon(inode); - if (!result) { + if (!result) { iput(inode); return ERR_PTR(-ENOMEM); } @@ -178,9 +220,9 @@ xfs_fs_get_parent( return parent; } -struct export_operations xfs_export_operations = { - .decode_fh = xfs_fs_decode_fh, +const struct export_operations xfs_export_operations = { .encode_fh = xfs_fs_encode_fh, + .fh_to_dentry = xfs_fs_fh_to_dentry, + .fh_to_parent = xfs_fs_fh_to_parent, .get_parent = xfs_fs_get_parent, - .get_dentry = xfs_fs_get_dentry, }; diff --git a/fs/xfs/linux-2.6/xfs_export.h b/fs/xfs/linux-2.6/xfs_export.h index 2f36071a86f..3272b6ae7a3 100644 --- a/fs/xfs/linux-2.6/xfs_export.h +++ b/fs/xfs/linux-2.6/xfs_export.h @@ -59,50 +59,14 @@ * a subdirectory) or use the "fsid" export option. */ +struct xfs_fid64 { + u64 ino; + u32 gen; + u64 parent_ino; + u32 parent_gen; +} __attribute__((packed)); + /* This flag goes on the wire. Don't play with it. */ #define XFS_FILEID_TYPE_64FLAG 0x80 /* NFS fileid has 64bit inodes */ -/* Calculate the length in u32 units of the fileid data */ -static inline int -xfs_fileid_length(int hasparent, int is64) -{ - return hasparent ? (is64 ? 6 : 4) : (is64 ? 3 : 2); -} - -/* - * Decode encoded inode information (either for the inode itself - * or the parent) into an xfs_fid_t structure. Advances and - * returns the new data pointer - */ -static inline __u32 * -xfs_fileid_decode_fid2(__u32 *p, xfs_fid_t *fid, int is64) -{ - fid->fid_len = sizeof(xfs_fid_t) - sizeof(fid->fid_len); - fid->fid_pad = 0; - fid->fid_ino = *p++; -#if XFS_BIG_INUMS - if (is64) - fid->fid_ino |= (((__u64)(*p++)) << 32); -#endif - fid->fid_gen = *p++; - return p; -} - -/* - * Encode inode information (either for the inode itself or the - * parent) into a fileid buffer. Advances and returns the new - * data pointer. - */ -static inline __u32 * -xfs_fileid_encode_inode(__u32 *p, struct inode *inode, int is64) -{ - *p++ = (__u32)inode->i_ino; -#if XFS_BIG_INUMS - if (is64) - *p++ = (__u32)(inode->i_ino >> 32); -#endif - *p++ = inode->i_generation; - return p; -} - #endif /* __XFS_EXPORT_H__ */ diff --git a/fs/xfs/linux-2.6/xfs_super.h b/fs/xfs/linux-2.6/xfs_super.h index c78c23310fe..3efcf45b14a 100644 --- a/fs/xfs/linux-2.6/xfs_super.h +++ b/fs/xfs/linux-2.6/xfs_super.h @@ -118,7 +118,7 @@ extern int xfs_blkdev_get(struct xfs_mount *, const char *, extern void xfs_blkdev_put(struct block_device *); extern void xfs_blkdev_issue_flush(struct xfs_buftarg *); -extern struct export_operations xfs_export_operations; +extern const struct export_operations xfs_export_operations; #define XFS_M(sb) ((struct xfs_mount *)((sb)->s_fs_info)) |