summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-07-29 03:58:24 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-07-29 03:58:24 +0000
commit1c5c0c934f91fbce2825acbb849e98781e774c1d (patch)
tree12b5ae03516d4103bc070e4579ae1f7f71c27d24 /fs
parent4fe70c31de87823ac9e804f4795589ba74dc6971 (diff)
Merge with 2.1.47. Some more cleanup and module fixes.
Diffstat (limited to 'fs')
-rw-r--r--fs/dcache.c49
-rw-r--r--fs/fat/inode.c12
-rw-r--r--fs/fat/misc.c2
-rw-r--r--fs/hpfs/hpfs_fs.c36
-rw-r--r--fs/inode.c54
-rw-r--r--fs/lockd/svcsubs.c16
-rw-r--r--fs/minix/bitmap.c3
-rw-r--r--fs/minix/dir.c1
-rw-r--r--fs/minix/file.c8
-rw-r--r--fs/minix/namei.c3
-rw-r--r--fs/msdos/namei.c240
-rw-r--r--fs/namei.c30
-rw-r--r--fs/nfs/write.c2
-rw-r--r--fs/nfsd/export.c100
-rw-r--r--fs/nfsd/lockd.c2
-rw-r--r--fs/nfsd/nfs3proc.c7
-rw-r--r--fs/nfsd/nfsctl.c5
-rw-r--r--fs/nfsd/nfsfh.c84
-rw-r--r--fs/nfsd/nfsproc.c167
-rw-r--r--fs/nfsd/nfsxdr.c6
-rw-r--r--fs/nfsd/vfs.c584
-rw-r--r--fs/open.c35
-rw-r--r--fs/romfs/inode.c124
-rw-r--r--fs/super.c1
24 files changed, 815 insertions, 756 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 395aed829..97937822f 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -17,6 +17,7 @@
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/malloc.h>
+#include <linux/init.h>
/*
* This is the single most critical data structure when it comes
@@ -229,6 +230,46 @@ struct dentry * d_lookup(struct dentry * dir, struct qstr * name)
return __dlookup(d_hash(dir, name->hash), dir, name);
}
+/*
+ * An insecure source has sent us a dentry, here we verify it.
+ *
+ * This is just to make knfsd able to have the dentry pointer
+ * in the NFS file handle.
+ *
+ * NOTE! Do _not_ dereference the pointers before we have
+ * validated them. We can test the pointer values, but we
+ * must not actually use them until we have found a valid
+ * copy of the pointer in kernel space..
+ */
+int d_validate(struct dentry *dentry, struct dentry *dparent,
+ unsigned int hash, unsigned int len)
+{
+ struct list_head *base = d_hash(dparent, hash);
+ struct list_head *lhp = base;
+
+ while ((lhp = lhp->next) != base) {
+ if (dentry == list_entry(lhp, struct dentry, d_hash))
+ goto found_it;
+ }
+
+ /* Special case, local mount points don't live in the hashes.
+ * So if we exhausted the chain, search the super blocks.
+ */
+ if (dentry && dentry == dparent) {
+ struct super_block *sb;
+
+ for (sb = super_blocks + 0; sb < super_blocks + NR_SUPER; sb++) {
+ if (sb->s_root == dentry)
+ goto found_it;
+ }
+ }
+ return 0;
+found_it:
+ return (dentry->d_parent == dparent) &&
+ (dentry->d_name.hash == hash) &&
+ (dentry->d_name.len == len);
+}
+
static inline void d_insert_to_parent(struct dentry * entry, struct dentry * parent)
{
list_add(&entry->d_hash, d_hash(dget(parent), entry->d_name.hash));
@@ -313,13 +354,17 @@ void d_move(struct dentry * dentry, struct dentry * newdir, struct qstr * newnam
d_insert_to_parent(dentry, newdir);
}
+/*
+ * This is broken in more ways than one. Unchecked recursion,
+ * unchecked buffer size. Get rid of it.
+ */
int d_path(struct dentry * entry, struct dentry * chroot, char * buf)
{
if (IS_ROOT(entry) || (chroot && entry == chroot)) {
*buf = '/';
return 1;
} else {
- int len = d_path(entry->d_parent, chroot, buf);
+ int len = d_path(entry->d_covers->d_parent, chroot, buf);
buf += len;
if (len > 1) {
@@ -331,7 +376,7 @@ int d_path(struct dentry * entry, struct dentry * chroot, char * buf)
}
}
-void dcache_init(void)
+__initfunc(void dcache_init(void))
{
int i;
struct list_head *d = dentry_hashtable;
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index e35722aff..8ffd91e3d 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -44,8 +44,18 @@ void fat_put_inode(struct inode *inode)
MSDOS_I(inode)->i_linked = NULL;
}
if (MSDOS_I(inode)->i_busy) fat_cache_inval_inode(inode);
- return;
}
+}
+
+void fat_delete_inode(struct inode *inode)
+{
+ struct inode *depend, *linked;
+ struct super_block *sb;
+
+ depend = MSDOS_I(inode)->i_depend;
+ linked = MSDOS_I(inode)->i_linked;
+ sb = inode->i_sb;
+
inode->i_size = 0;
fat_truncate(inode);
if (depend) {
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index 034f62c1f..ee0df4a15 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -40,7 +40,7 @@ void fat_fs_panic(struct super_block *s,const char *msg)
not_ro = !(s->s_flags & MS_RDONLY);
if (not_ro) s->s_flags |= MS_RDONLY;
- printk("Filesystem panic (dev %s).", kdevname(s->s_dev));
+ printk("Filesystem panic (dev %s).\n %s\n", kdevname(s->s_dev), msg);
if (not_ro)
printk(" File system has been set read-only\n");
}
diff --git a/fs/hpfs/hpfs_fs.c b/fs/hpfs/hpfs_fs.c
index 878a3f069..70f293821 100644
--- a/fs/hpfs/hpfs_fs.c
+++ b/fs/hpfs/hpfs_fs.c
@@ -128,15 +128,16 @@ typedef void nonconst;
static void hpfs_read_inode(struct inode *);
static void hpfs_put_super(struct super_block *);
-static void hpfs_statfs(struct super_block *, struct statfs *, int);
+static int hpfs_statfs(struct super_block *, struct statfs *, int);
static int hpfs_remount_fs(struct super_block *, int *, char *);
static const struct super_operations hpfs_sops =
{
hpfs_read_inode, /* read_inode */
- NULL, /* notify_change */
NULL, /* write_inode */
NULL, /* put_inode */
+ NULL, /* delete_inode */
+ NULL, /* notify_change */
hpfs_put_super, /* put_super */
NULL, /* write_super */
hpfs_statfs, /* statfs */
@@ -175,6 +176,7 @@ static const struct inode_operations hpfs_file_iops =
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
generic_readpage, /* readpage */
NULL, /* writepage */
(int (*)(struct inode *, int))
@@ -189,7 +191,7 @@ static long hpfs_dir_read(struct inode *inode, struct file *filp,
char *buf, unsigned long count);
static int hpfs_readdir(struct inode *inode, struct file *filp,
void *dirent, filldir_t filldir);
-static int hpfs_lookup(struct inode *, const char *, int, struct inode **);
+static int hpfs_lookup(struct inode *, struct dentry *);
static const struct file_operations hpfs_dir_ops =
{
@@ -485,10 +487,10 @@ struct super_block *hpfs_read_super(struct super_block *s,
* all set. try it out.
*/
- s->s_mounted = iget(s, s->s_hpfs_root);
+ s->s_root = d_alloc_root(iget(s, s->s_hpfs_root), NULL);
unlock_super(s);
- if (!s->s_mounted) {
+ if (!s->s_root) {
printk("HPFS: hpfs_read_super: inode get failed\n");
s->s_dev = 0;
MOD_DEC_USE_COUNT;
@@ -501,7 +503,8 @@ struct super_block *hpfs_read_super(struct super_block *s,
root_dno = fnode_dno(dev, s->s_hpfs_root);
if (root_dno)
- de = map_dirent(s->s_mounted, root_dno, "\001\001", 2, &qbh);
+ de = map_dirent(s->s_root->d_inode, root_dno,
+ "\001\001", 2, &qbh);
if (!root_dno || !de) {
printk("HPFS: "
"hpfs_read_super: root dir isn't in the root dir\n");
@@ -510,9 +513,9 @@ struct super_block *hpfs_read_super(struct super_block *s,
return 0;
}
- s->s_mounted->i_atime = local_to_gmt(de->read_date);
- s->s_mounted->i_mtime = local_to_gmt(de->write_date);
- s->s_mounted->i_ctime = local_to_gmt(de->creation_date);
+ s->s_root->d_inode->i_atime = local_to_gmt(de->read_date);
+ s->s_root->d_inode->i_mtime = local_to_gmt(de->write_date);
+ s->s_root->d_inode->i_ctime = local_to_gmt(de->creation_date);
brelse4(&qbh);
return s;
@@ -739,7 +742,7 @@ static void hpfs_put_super(struct super_block *s)
* directory band -- not exactly right but pretty analogous.
*/
-static void hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz)
+static int hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz)
{
struct statfs tmp;
@@ -763,7 +766,8 @@ static void hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz)
tmp.f_files = s->s_hpfs_dirband_size;
tmp.f_ffree = s->s_hpfs_n_free_dnodes;
tmp.f_namelen = 254;
- copy_to_user(buf, &tmp, bufsiz);
+
+ return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
}
/*
@@ -1115,17 +1119,17 @@ static secno bplus_lookup(struct inode *inode, struct bplus_header *b,
* the boondocks.)
*/
-static int hpfs_lookup(struct inode *dir, const char *name, int len,
- struct inode **result)
+static int hpfs_lookup(struct inode *dir, struct dentry *dentry)
{
struct quad_buffer_head qbh;
struct hpfs_dirent *de;
struct inode *inode;
ino_t ino;
+ const char *name = dentry->d_name.name;
+ int len = dentry->d_name.len;
/* In case of madness */
- *result = 0;
if (dir == 0)
return -ENOENT;
if (!S_ISDIR(dir->i_mode))
@@ -1197,7 +1201,7 @@ static int hpfs_lookup(struct inode *dir, const char *name, int len,
* Made it.
*/
- *result = inode;
+ d_instantiate(dentry, inode);
iput(dir);
return 0;
@@ -1697,7 +1701,7 @@ static void *map_4sectors(kdev_t dev, unsigned secno,
if (!data)
goto bail;
- qbh->bh[0] = bh = breada(dev, secno, 512, 0, UINT_MAX);
+ qbh->bh[0] = bh = bread(dev, secno, 512);
if (!bh)
goto bail0;
memcpy(data, bh->b_data, 512);
diff --git a/fs/inode.c b/fs/inode.c
index 8813bbd45..8fc081b77 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -141,32 +141,31 @@ static inline void write_inode(struct inode *inode)
inode->i_sb->s_op->write_inode(inode);
}
+static inline void sync_one(struct list_head *head, struct list_head *clean,
+ struct list_head *placement, struct inode *inode)
+{
+ list_del(placement);
+ if (test_bit(I_LOCK, &inode->i_state)) {
+ list_add(placement, head);
+ spin_unlock(&inode_lock);
+ __wait_on_inode(inode);
+ } else {
+ list_add(placement, clean);
+ clear_bit(I_DIRTY, &inode->i_state);
+ set_bit(I_LOCK, &inode->i_state);
+ spin_unlock(&inode_lock);
+ write_inode(inode);
+ unlock_inode(inode);
+ }
+ spin_lock(&inode_lock);
+}
+
static inline void sync_list(struct list_head *head, struct list_head *clean)
{
struct list_head * tmp;
- while ((tmp = head->prev) != head) {
- struct inode *inode = list_entry(tmp, struct inode, i_list);
- list_del(tmp);
-
- /*
- * If the inode is locked, it's already being written out.
- * We have to wait for it, though.
- */
- if (test_bit(I_LOCK, &inode->i_state)) {
- list_add(tmp, head);
- spin_unlock(&inode_lock);
- __wait_on_inode(inode);
- } else {
- list_add(tmp, clean);
- clear_bit(I_DIRTY, &inode->i_state);
- set_bit(I_LOCK, &inode->i_state);
- spin_unlock(&inode_lock);
- write_inode(inode);
- unlock_inode(inode);
- }
- spin_lock(&inode_lock);
- }
+ while ((tmp = head->prev) != head)
+ sync_one(head, clean, tmp, list_entry(tmp, struct inode, i_list));
}
/*
@@ -182,6 +181,17 @@ void sync_inodes(kdev_t dev)
}
/*
+ * Needed by knfsd
+ */
+void write_inode_now(struct inode *inode)
+{
+ spin_lock(&inode_lock);
+ if (test_bit(I_DIRTY, &inode->i_state))
+ sync_one(&inode_dirty, &inode_in_use, &inode->i_list, inode);
+ spin_unlock(&inode_lock);
+}
+
+/*
* This is called by the filesystem to tell us
* that the inode is no longer useful. We just
* terminate it with extreme predjudice.
diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c
index afc3fb2f0..3526867ca 100644
--- a/fs/lockd/svcsubs.c
+++ b/fs/lockd/svcsubs.c
@@ -24,7 +24,7 @@
* Global file hash table
*/
#define FILE_NRHASH 32
-#define FILE_HASH(dev, ino) (((dev) + (ino)) & FILE_NRHASH)
+#define FILE_HASH(dhash) ((dhash) & FILE_NRHASH)
static struct nlm_file * nlm_files[FILE_NRHASH];
static struct semaphore nlm_file_sema = MUTEX;
@@ -43,21 +43,21 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
{
struct nlm_file *file;
struct knfs_fh *fh = (struct knfs_fh *) f;
- unsigned int hash = FILE_HASH(fh->fh_dev, fh->fh_ino);
+ unsigned int hash = FILE_HASH(fh->fh_dhash);
u32 nfserr;
- dprintk("lockd: nlm_file_lookup(%04x/%ld)\n", fh->fh_dev, fh->fh_ino);
+ dprintk("lockd: nlm_file_lookup(%p)\n", fh->fh_dentry);
/* Lock file table */
down(&nlm_file_sema);
for (file = nlm_files[hash]; file; file = file->f_next) {
- if (file->f_handle.fh_ino == fh->fh_ino
+ if (file->f_handle.fh_dentry == fh->fh_dentry
&& !memcmp(&file->f_handle, fh, sizeof(*fh)))
goto found;
}
- dprintk("lockd: creating file for %04x/%ld\n", fh->fh_dev, fh->fh_ino);
+ dprintk("lockd: creating file for %p\n", fh->fh_dentry);
if (!(file = (struct nlm_file *) kmalloc(sizeof(*file), GFP_KERNEL))) {
up(&nlm_file_sema);
return nlm_lck_denied_nolocks;
@@ -93,11 +93,11 @@ found:
static inline void
nlm_delete_file(struct nlm_file *file)
{
- struct inode *inode = nlmsvc_file_inode(file);
+ struct dentry *dentry = file->f_file.f_dentry;
struct nlm_file **fp, *f;
- dprintk("lockd: closing file %04x/%ld\n", inode->i_dev, inode->i_ino);
- fp = nlm_files + FILE_HASH(inode->i_dev, inode->i_ino);
+ dprintk("lockd: closing file %p\n", dentry);
+ fp = nlm_files + FILE_HASH(dentry->d_name.hash);
while ((f = *fp) != NULL) {
if (f == file) {
*fp = file->f_next;
diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c
index f47f779d5..9e27ee118 100644
--- a/fs/minix/bitmap.c
+++ b/fs/minix/bitmap.c
@@ -191,7 +191,7 @@ void minix_free_inode(struct inode * inode)
printk("free_inode: inode has no device\n");
return;
}
- if (inode->i_count != 1) {
+ if (inode->i_count > 1) {
printk("free_inode: inode has count=%d\n",inode->i_count);
return;
}
@@ -251,7 +251,6 @@ struct inode * minix_new_inode(const struct inode * dir)
iput(inode);
return NULL;
}
- inode->i_count = 1;
inode->i_nlink = 1;
inode->i_dev = sb->s_dev;
inode->i_uid = current->fsuid;
diff --git a/fs/minix/dir.c b/fs/minix/dir.c
index ec5113c4a..31ac394a2 100644
--- a/fs/minix/dir.c
+++ b/fs/minix/dir.c
@@ -92,5 +92,6 @@ static int minix_readdir(struct inode * inode, struct file * filp,
} while (offset < 1024 && filp->f_pos < inode->i_size);
brelse(bh);
}
+ UPDATE_ATIME(inode);
return 0;
}
diff --git a/fs/minix/file.c b/fs/minix/file.c
index 7ca7cb075..ecd0d24d9 100644
--- a/fs/minix/file.c
+++ b/fs/minix/file.c
@@ -108,7 +108,13 @@ static long minix_file_write(struct inode * inode, struct file * filp,
}
}
p = (pos % BLOCK_SIZE) + bh->b_data;
- copy_from_user(p,buf,c);
+ c -= copy_from_user(p,buf,c);
+ if (!c) {
+ brelse(bh);
+ if (!written)
+ written = -EFAULT;
+ break;
+ }
update_vm_cache(inode, pos, p, c);
mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 0);
diff --git a/fs/minix/namei.c b/fs/minix/namei.c
index 718d3dd07..19c7df247 100644
--- a/fs/minix/namei.c
+++ b/fs/minix/namei.c
@@ -109,7 +109,7 @@ int minix_lookup(struct inode * dir, struct dentry *dentry)
bh = minix_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de);
if (bh) {
- unsigned long ino = le32_to_cpu(de->inode);
+ int ino = de->inode;
brelse (bh);
inode = iget(dir->i_sb, ino);
@@ -593,6 +593,7 @@ int minix_link(struct inode * inode, struct inode * dir,
inode->i_nlink++;
inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode);
+ inode->i_count++;
d_instantiate(dentry, inode);
return 0;
}
diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c
index 9481a763c..637649dae 100644
--- a/fs/msdos/namei.c
+++ b/fs/msdos/namei.c
@@ -53,9 +53,10 @@ void msdos_put_super(struct super_block *sb)
struct super_operations msdos_sops = {
msdos_read_inode,
- fat_notify_change,
fat_write_inode,
fat_put_inode,
+ fat_delete_inode,
+ fat_notify_change,
msdos_put_super,
NULL, /* added in 0.96c */
fat_statfs,
@@ -190,79 +191,56 @@ static int msdos_find(struct inode *dir,const char *name,int len,
}
/***** Get inode using directory and name */
-int msdos_lookup(struct inode *dir,const char *name,int len,
- struct inode **result)
+int msdos_lookup(struct inode *dir,struct dentry *dentry)
{
struct super_block *sb = dir->i_sb;
int ino,res;
struct msdos_dir_entry *de;
struct buffer_head *bh;
- struct inode *next;
+ struct inode *next, *inode;
PRINTK (("msdos_lookup\n"));
- *result = NULL;
- if (!dir) return -ENOENT;
- if (!S_ISDIR(dir->i_mode)) {
- iput(dir);
- return -ENOENT;
- }
- PRINTK (("msdos_lookup 2\n"));
- if (len == 1 && name[0] == '.') {
- *result = dir;
- return 0;
- }
- if (len == 2 && name[0] == '.' && name[1] == '.') {
- ino = fat_parent_ino(dir,0);
- iput(dir);
- if (ino < 0) return ino;
- if (!(*result = iget(dir->i_sb,ino))) return -EACCES;
+ if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,&bh,&de,&ino)) < 0) {
+ d_add(dentry, NULL);
return 0;
}
- PRINTK (("msdos_lookup 3\n"));
- if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) {
- iput(dir);
- return res;
- }
PRINTK (("msdos_lookup 4\n"));
if (bh)
fat_brelse(sb, bh);
PRINTK (("msdos_lookup 4.5\n"));
- if (!(*result = iget(dir->i_sb,ino))) {
- iput(dir);
+ if (!(inode = iget(dir->i_sb,ino)))
return -EACCES;
- }
PRINTK (("msdos_lookup 5\n"));
- if (!(*result)->i_sb ||
- ((*result)->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
+ if (!inode->i_sb ||
+ (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
/* crossed a mount point into a non-msdos fs */
- iput(dir);
+ d_add(dentry, inode);
return 0;
}
- if (MSDOS_I(*result)->i_busy) { /* mkdir in progress */
- iput(*result);
- iput(dir);
- return -ENOENT;
+ if (MSDOS_I(inode)->i_busy) { /* mkdir in progress */
+ iput(inode);
+ d_add(dentry, NULL);
+ return 0;
}
PRINTK (("msdos_lookup 6\n"));
- while (MSDOS_I(*result)->i_old) {
- next = MSDOS_I(*result)->i_old;
- iput(*result);
- if (!(*result = iget(next->i_sb,next->i_ino))) {
+ while (MSDOS_I(inode)->i_old) {
+ next = MSDOS_I(inode)->i_old;
+ iput(inode);
+ if (!(inode = iget(next->i_sb,next->i_ino))) {
fat_fs_panic(dir->i_sb,"msdos_lookup: Can't happen");
- iput(dir);
return -ENOENT;
}
}
PRINTK (("msdos_lookup 7\n"));
- iput(dir);
+ d_add(dentry, inode);
PRINTK (("msdos_lookup 8\n"));
return 0;
}
/***** Creates a directory entry (name is already formatted). */
-static int msdos_create_entry(struct inode *dir, const char *name,int len,
+static int msdos_create_entry(struct inode *dir, const char *name,
int is_dir, int is_hid, struct inode **result)
{
struct super_block *sb = dir->i_sb;
@@ -270,6 +248,7 @@ static int msdos_create_entry(struct inode *dir, const char *name,int len,
struct msdos_dir_entry *de;
int res,ino;
+ *result = NULL;
if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) {
if (res != -ENOENT) return res;
if (dir->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
@@ -300,23 +279,22 @@ static int msdos_create_entry(struct inode *dir, const char *name,int len,
}
/***** Create a file or directory */
-int msdos_create(struct inode *dir,const char *name,int len,int mode,
- struct inode **result)
+int msdos_create(struct inode *dir,struct dentry *dentry,int mode)
{
struct super_block *sb = dir->i_sb;
struct buffer_head *bh;
struct msdos_dir_entry *de;
+ struct inode *inode;
char msdos_name[MSDOS_NAME];
int ino,res,is_hid;
if (!dir) return -ENOENT;
if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
- name,len,msdos_name,0,
- MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) {
- iput(dir);
+ dentry->d_name.name,dentry->d_name.len,
+ msdos_name,0,
+ MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0)
return res;
- }
- is_hid = (name[0]=='.') && (msdos_name[0]!='.');
+ is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
fat_lock_creation();
/* Scan for existing file twice, so that creating a file fails
* with -EINVAL if the other (dotfile/nondotfile) exists.
@@ -325,19 +303,17 @@ int msdos_create(struct inode *dir,const char *name,int len,int mode,
if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_HID) >= 0) {
fat_unlock_creation();
fat_brelse(sb, bh);
- iput(dir);
return is_hid ? -EEXIST : -EINVAL;
}
if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_NOTHID) >= 0) {
fat_unlock_creation();
fat_brelse(sb, bh);
- iput(dir);
return is_hid ? -EINVAL : -EEXIST;
}
- res = msdos_create_entry(dir,msdos_name,len,S_ISDIR(mode),is_hid,
- result);
+ res = msdos_create_entry(dir,msdos_name,S_ISDIR(mode),is_hid,
+ &inode);
fat_unlock_creation();
- iput(dir);
+ d_instantiate(dentry, inode);
return res;
}
@@ -388,7 +364,7 @@ static int msdos_empty(struct inode *dir)
}
/***** Remove a directory */
-int msdos_rmdir(struct inode *dir,const char *name,int len)
+int msdos_rmdir(struct inode *dir,struct dentry *dentry)
{
struct super_block *sb = dir->i_sb;
int res,ino;
@@ -399,11 +375,10 @@ int msdos_rmdir(struct inode *dir,const char *name,int len)
bh = NULL;
inode = NULL;
res = -EPERM;
- if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.')))
+ if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,
+ &bh,&de,&ino)) < 0)
goto rmdir_done;
- if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) goto rmdir_done;
- res = -ENOENT;
- if (!(inode = iget(dir->i_sb,ino))) goto rmdir_done;
+ inode = dentry->d_inode;
res = -ENOTDIR;
if (!S_ISDIR(inode->i_mode)) goto rmdir_done;
res = -EBUSY;
@@ -419,16 +394,15 @@ int msdos_rmdir(struct inode *dir,const char *name,int len)
mark_inode_dirty(dir);
de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, bh, 1);
+ d_delete(dentry);
res = 0;
rmdir_done:
fat_brelse(sb, bh);
- iput(dir);
- iput(inode);
return res;
}
/***** Make a directory */
-int msdos_mkdir(struct inode *dir,const char *name,int len,int mode)
+int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode)
{
struct super_block *sb = dir->i_sb;
struct buffer_head *bh;
@@ -438,37 +412,34 @@ int msdos_mkdir(struct inode *dir,const char *name,int len,int mode)
int ino,res,is_hid;
if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
- name,len,msdos_name,0,
- MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) {
- iput(dir);
+ dentry->d_name.name,dentry->d_name.len,
+ msdos_name,0,
+ MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0)
return res;
- }
- is_hid = (name[0]=='.') && (msdos_name[0]!='.');
+ is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
fat_lock_creation();
if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) {
fat_unlock_creation();
fat_brelse(sb, bh);
- iput(dir);
return -EEXIST;
}
- if ((res = msdos_create_entry(dir,msdos_name,len,1,is_hid,
+ if ((res = msdos_create_entry(dir,msdos_name,1,is_hid,
&inode)) < 0) {
fat_unlock_creation();
- iput(dir);
return res;
}
dir->i_nlink++;
inode->i_nlink = 2; /* no need to mark them dirty */
MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
if ((res = fat_add_cluster(inode)) < 0) goto mkdir_error;
- if ((res = msdos_create_entry(inode,MSDOS_DOT,1,1,0,&dot)) < 0)
+ if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0)
goto mkdir_error;
dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */
MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start;
dot->i_nlink = inode->i_nlink;
mark_inode_dirty(dot);
iput(dot);
- if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,2,1,0,&dot)) < 0)
+ if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,0,&dot)) < 0)
goto mkdir_error;
fat_unlock_creation();
dot->i_size = dir->i_size;
@@ -477,12 +448,10 @@ int msdos_mkdir(struct inode *dir,const char *name,int len,int mode)
mark_inode_dirty(dot);
MSDOS_I(inode)->i_busy = 0;
iput(dot);
- iput(inode);
- iput(dir);
+ d_instantiate(dentry, inode);
return 0;
mkdir_error:
- iput(inode);
- if (msdos_rmdir(dir,name,len) < 0)
+ if (msdos_rmdir(dir,dentry) < 0)
fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
fat_unlock_creation();
return res;
@@ -491,8 +460,7 @@ mkdir_error:
/***** Unlink a file */
static int msdos_unlinkx(
struct inode *dir,
- const char *name,
- int len,
+ struct dentry *dentry,
int nospc) /* Flag special file ? */
{
struct super_block *sb = dir->i_sb;
@@ -503,12 +471,10 @@ static int msdos_unlinkx(
bh = NULL;
inode = NULL;
- if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0)
- goto unlink_done;
- if (!(inode = iget(dir->i_sb,ino))) {
- res = -ENOENT;
+ if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,
+ &bh,&de,&ino)) < 0)
goto unlink_done;
- }
+ inode = dentry->d_inode;
if (!S_ISREG(inode->i_mode) && nospc){
res = -EPERM;
goto unlink_done;
@@ -524,28 +490,28 @@ static int msdos_unlinkx(
mark_inode_dirty(dir);
de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, bh, 1);
+ d_delete(dentry); /* This also frees the inode */
unlink_done:
fat_brelse(sb, bh);
- iput(inode);
- iput(dir);
return res;
}
/***** Unlink, as called for msdosfs */
-int msdos_unlink(struct inode *dir,const char *name,int len)
+int msdos_unlink(struct inode *dir,struct dentry *dentry)
{
- return msdos_unlinkx (dir,name,len,1);
+ return msdos_unlinkx (dir,dentry,1);
}
/***** Unlink, as called for umsdosfs */
-int msdos_unlink_umsdos(struct inode *dir,const char *name,int len)
+int msdos_unlink_umsdos(struct inode *dir,struct dentry *dentry)
{
- return msdos_unlinkx (dir,name,len,0);
+ return msdos_unlinkx (dir,dentry,0);
}
/***** Rename within a directory */
-static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len,
- struct inode *new_dir,char *new_name,int new_len,
+static int rename_same_dir(struct inode *old_dir,char *old_name,
+ struct dentry *old_dentry,
+ struct inode *new_dir,char *new_name,struct dentry *new_dentry,
struct buffer_head *old_bh,
struct msdos_dir_entry *old_de,int old_ino,int is_hid)
{
@@ -563,10 +529,7 @@ static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len,
return -ENOENT;
}
if (exists) {
- if (!(new_inode = iget(new_dir->i_sb,new_ino))) {
- fat_brelse(sb, new_bh);
- return -EIO;
- }
+ new_inode = new_dentry->d_inode;
error = S_ISDIR(new_inode->i_mode)
? (old_de->attr & ATTR_DIR)
? msdos_empty(new_inode)
@@ -576,7 +539,6 @@ static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len,
: 0;
if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
if (error) {
- iput(new_inode);
fat_brelse(sb, new_bh);
return error;
}
@@ -589,50 +551,49 @@ static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len,
mark_inode_dirty(new_inode);
new_de->name[0] = DELETED_FLAG;
fat_mark_buffer_dirty(sb, new_bh, 1);
- iput(new_inode);
fat_brelse(sb, new_bh);
}
memcpy(old_de->name,new_name,MSDOS_NAME);
+ /* Update the dcache */
+ d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
+ d_delete(new_dentry);
set_hid:
old_de->attr = is_hid
? (old_de->attr | ATTR_HIDDEN)
: (old_de->attr &~ ATTR_HIDDEN);
fat_mark_buffer_dirty(sb, old_bh, 1);
/* update binary info for conversion, i_attrs */
- if ((old_inode = iget(old_dir->i_sb,old_ino)) != NULL) {
- MSDOS_I(old_inode)->i_attrs = is_hid
- ? (MSDOS_I(old_inode)->i_attrs | ATTR_HIDDEN)
- : (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN);
- iput(old_inode);
- }
+ old_inode = old_dentry->d_inode;
+ MSDOS_I(old_inode)->i_attrs = is_hid
+ ? (MSDOS_I(old_inode)->i_attrs | ATTR_HIDDEN)
+ : (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN);
return 0;
}
/***** Rename across directories - a nonphysical move */
-static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
- struct inode *new_dir,char *new_name,int new_len,
+static int rename_diff_dir(struct inode *old_dir,char *old_name,
+ struct dentry *old_dentry,
+ struct inode *new_dir,char *new_name,struct dentry *new_dentry,
struct buffer_head *old_bh,
struct msdos_dir_entry *old_de,int old_ino,int is_hid)
{
struct super_block *sb = old_dir->i_sb;
struct buffer_head *new_bh,*free_bh,*dotdot_bh;
struct msdos_dir_entry *new_de,*free_de,*dotdot_de;
- struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode,*walk;
+ struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode;
+ struct dentry *walk;
int new_ino,free_ino,dotdot_ino;
int error,exists,ino;
if (old_dir->i_dev != new_dir->i_dev) return -EINVAL;
if (old_ino == new_dir->i_ino) return -EINVAL;
- if (!(walk = iget(new_dir->i_sb,new_dir->i_ino))) return -EIO;
+ walk = new_dentry;
/* prevent moving directory below itself */
- while (walk->i_ino != MSDOS_ROOT_INO) {
- ino = fat_parent_ino(walk,1);
- iput(walk);
- if (ino < 0) return ino;
- if (ino == old_ino) return -EINVAL;
- if (!(walk = iget(new_dir->i_sb,ino))) return -EIO;
+ for (;;) {
+ if (walk == old_dentry) return -EINVAL;
+ if (walk == walk->d_parent) break;
+ walk = walk->d_parent;
}
- iput(walk);
/* find free spot */
while ((error = fat_scan(new_dir,NULL,&free_bh,&free_de,&free_ino,
SCAN_ANY)) < 0) {
@@ -641,14 +602,8 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
if (error) return error;
}
exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
- if (!(old_inode = iget(old_dir->i_sb,old_ino))) {
- fat_brelse(sb, free_bh);
- if (exists)
- fat_brelse(sb, new_bh);
- return -EIO;
- }
+ old_inode = old_dentry->d_inode;
if (*(unsigned char *) old_de->name == DELETED_FLAG) {
- iput(old_inode);
fat_brelse(sb, free_bh);
if (exists)
fat_brelse(sb, new_bh);
@@ -656,11 +611,7 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
}
new_inode = NULL; /* to make GCC happy */
if (exists) { /* Trash the old file! */
- if (!(new_inode = iget(new_dir->i_sb,new_ino))) {
- iput(old_inode);
- fat_brelse(sb, new_bh);
- return -EIO;
- }
+ new_inode = new_dentry->d_inode;
error = S_ISDIR(new_inode->i_mode)
? (old_de->attr & ATTR_DIR)
? msdos_empty(new_inode)
@@ -670,8 +621,6 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
: 0;
if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
if (error) {
- iput(new_inode);
- iput(old_inode);
fat_brelse(sb, new_bh);
return error;
}
@@ -690,10 +639,8 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
free_de->name[0] = DELETED_FLAG;
/* Don't mark free_bh as dirty. Both states are supposed to be equivalent. */
fat_brelse(sb, free_bh);
- if (exists) {
- iput(new_inode);
+ if (exists)
fat_brelse(sb, new_bh);
- }
return -EIO;
}
if (exists && S_ISDIR(new_inode->i_mode)) {
@@ -715,7 +662,6 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
/* Two references now exist to free_inode so increase count */
free_inode->i_count++;
/* free_inode is put after putting new_inode and old_inode */
- iput(new_inode);
fat_brelse(sb, new_bh);
}
if (S_ISDIR(old_inode->i_mode)) {
@@ -737,16 +683,18 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
iput(dotdot_inode);
fat_brelse(sb, dotdot_bh);
}
+ /* Update the dcache */
+ d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
+ d_delete(new_dentry);
error = 0;
rename_done:
fat_brelse(sb, free_bh);
- iput(old_inode);
return error;
}
/***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
-int msdos_rename(struct inode *old_dir,const char *old_name,int old_len,
- struct inode *new_dir,const char *new_name,int new_len)
+int msdos_rename(struct inode *old_dir,struct dentry *old_dentry,
+ struct inode *new_dir,struct dentry *new_dentry)
{
struct super_block *sb = old_dir->i_sb;
char old_msdos_name[MSDOS_NAME],new_msdos_name[MSDOS_NAME];
@@ -756,28 +704,30 @@ int msdos_rename(struct inode *old_dir,const char *old_name,int old_len,
int is_hid,old_hid; /* if new file and old file are hidden */
if ((error = msdos_format_name(MSDOS_SB(old_dir->i_sb)->options.name_check,
- old_name,old_len,old_msdos_name,1,
+ old_dentry->d_name.name,
+ old_dentry->d_name.len,old_msdos_name,1,
MSDOS_SB(old_dir->i_sb)->options.dotsOK))
< 0) goto rename_done;
if ((error = msdos_format_name(MSDOS_SB(new_dir->i_sb)->options.name_check,
- new_name,new_len,new_msdos_name,0,
+ new_dentry->d_name.name,
+ new_dentry->d_name.len,new_msdos_name,0,
MSDOS_SB(new_dir->i_sb)->options.dotsOK))
< 0) goto rename_done;
- is_hid = (new_name[0]=='.') && (new_msdos_name[0]!='.');
- old_hid = (old_name[0]=='.') && (old_msdos_name[0]!='.');
+ is_hid = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.');
+ old_hid = (old_dentry->d_name.name[0]=='.') && (old_msdos_name[0]!='.');
if ((error = fat_scan(old_dir,old_msdos_name,&old_bh,&old_de,
&old_ino,old_hid?SCAN_HID:SCAN_NOTHID)) < 0) goto rename_done;
fat_lock_creation();
if (old_dir == new_dir)
- error = rename_same_dir(old_dir,old_msdos_name,old_len,new_dir,
- new_msdos_name,new_len,old_bh,old_de,old_ino,is_hid);
- else error = rename_diff_dir(old_dir,old_msdos_name,old_len,new_dir,
- new_msdos_name,new_len,old_bh,old_de,old_ino,is_hid);
+ error = rename_same_dir(old_dir,old_msdos_name,old_dentry,
+ new_dir,new_msdos_name,new_dentry,
+ old_bh,old_de,old_ino,is_hid);
+ else error = rename_diff_dir(old_dir,old_msdos_name,old_dentry,
+ new_dir,new_msdos_name,new_dentry,
+ old_bh,old_de,old_ino,is_hid);
fat_unlock_creation();
fat_brelse(sb, old_bh);
rename_done:
- iput(old_dir);
- iput(new_dir);
return error;
}
diff --git a/fs/namei.c b/fs/namei.c
index 2ec173f0d..8870e3a99 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -338,26 +338,39 @@ done_error:
return result;
}
-/*
- * This should check "link_count", but doesn't do that yet..
- */
static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry)
{
struct inode * inode = dentry->d_inode;
if (inode && inode->i_op && inode->i_op->follow_link) {
- struct dentry *result;
+ if (current->link_count < 5) {
+ struct dentry * result;
- /* This eats the base */
- result = inode->i_op->follow_link(inode, base);
- base = dentry;
- dentry = result;
+ current->link_count++;
+ /* This eats the base */
+ result = inode->i_op->follow_link(inode, base);
+ current->link_count--;
+ dput(dentry);
+ return result;
+ }
+ dput(dentry);
+ dentry = ERR_PTR(-ELOOP);
}
dput(base);
return dentry;
}
/*
+ * Allow a filesystem to translate the character set of
+ * a file name. This allows for filesystems that are not
+ * case-sensitive, for example.
+ *
+ * This is only a dummy define right now, but eventually
+ * it might become something like "(parent)->d_charmap[c]"
+ */
+#define name_translate_char(parent, c) (c)
+
+/*
* Name resolution.
*
* This is the basic name resolution function, turning a pathname
@@ -397,6 +410,7 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, int follo
c = *name;
do {
len++; name++;
+ c = name_translate_char(base, c);
hash = partial_name_hash(c, hash);
c = *name;
} while (c && (c != '/'));
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 9241c679e..449fc0d4e 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -424,7 +424,6 @@ nfs_writepage(struct inode *inode, struct page *page)
/*
* Update and possibly write a cached page of an NFS file.
- * The page is already locked when we get here.
*
* XXX: Keep an eye on generic_file_read to make sure it doesn't do bad
* things with a page scheduled for an RPC call (e.g. invalidate it).
@@ -441,6 +440,7 @@ nfs_updatepage(struct inode *inode, struct page *page, const char *buffer,
inode->i_dev, inode->i_ino,
count, page->offset+offset, sync);
+ set_bit(PG_locked, &page->flags);
page_addr = (u8 *) page_address(page);
/* If wsize is smaller than page size, update and write
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index c83150b5f..221483587 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -90,9 +90,25 @@ exp_get(svc_client *clp, dev_t dev, ino_t ino)
}
/*
+ * Look up the root inode of the parent fs.
+ * We have to go through iget in order to allow for wait_on_inode.
+ */
+static inline int
+nfsd_parentdev(dev_t *devp)
+{
+ struct super_block *sb;
+
+ if (!(sb = get_super(*devp)) || !sb->s_root->d_covers)
+ return 0;
+ if (*devp == sb->s_root->d_covers->d_inode->i_dev)
+ return 0;
+ *devp = sb->s_root->d_covers->d_inode->i_dev;
+ return 1;
+}
+
+/*
* Find the parent export entry for a given fs. This function is used
* only by the export syscall to keep the export tree consistent.
- * nfsd_parentdev(dev) returns the device on which dev is mounted.
*/
static svc_export *
exp_parent(svc_client *clp, dev_t dev)
@@ -118,6 +134,7 @@ exp_export(struct nfsctl_export *nxp)
svc_client *clp;
svc_export *exp, *parent;
svc_export **head;
+ struct dentry *dentry = NULL;
struct inode *inode = NULL;
int i, err;
dev_t dev;
@@ -128,7 +145,7 @@ exp_export(struct nfsctl_export *nxp)
!exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
return -EINVAL;
- dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n",
+ dprintk("exp_export called for %s:%s (%x/%d fl %x).\n",
nxp->ex_client, nxp->ex_path,
nxp->ex_dev, nxp->ex_ino, nxp->ex_flags);
dev = nxp->ex_dev;
@@ -161,8 +178,19 @@ exp_export(struct nfsctl_export *nxp)
goto finish;
}
- /* Look up the inode */
- if (!(inode = nfsd_iget(nxp->ex_dev, nxp->ex_ino))) {
+ /* Look up the dentry */
+ dentry = lookup_dentry(nxp->ex_path, NULL, 0);
+ if (IS_ERR(dentry)) {
+ err = -EINVAL;
+ goto finish;
+ }
+ inode = dentry->d_inode;
+ if(!inode) {
+ err = -ENOENT;
+ goto finish;
+ }
+ if(inode->i_dev != nxp->ex_dev || inode->i_ino != nxp->ex_ino) {
+ /* I'm just being paranoid... */
err = -EINVAL;
goto finish;
}
@@ -175,9 +203,9 @@ exp_export(struct nfsctl_export *nxp)
/* If this is a sub-export, must be root of FS */
if ((parent = exp_parent(clp, dev)) != NULL) {
- struct super_block *sb;
+ struct super_block *sb = inode->i_sb;
- if ((sb = inode->i_sb) && (inode != sb->s_mounted)) {
+ if (sb && (inode != sb->s_root->d_inode)) {
err = -EINVAL;
goto finish;
}
@@ -192,7 +220,7 @@ exp_export(struct nfsctl_export *nxp)
strcpy(exp->ex_path, nxp->ex_path);
exp->ex_client = clp;
exp->ex_parent = parent;
- exp->ex_inode = inode;
+ exp->ex_dentry = dentry;
exp->ex_flags = nxp->ex_flags;
exp->ex_dev = dev;
exp->ex_ino = ino;
@@ -219,9 +247,10 @@ exp_export(struct nfsctl_export *nxp)
err = 0;
finish:
- /* Release inode */
- if (err < 0 && inode)
- iput(inode);
+ /* Release dentry */
+ if (err < 0 && dentry)
+ dput(dentry);
+
/* Unlock hashtable */
exp_unlock();
return err;
@@ -236,6 +265,7 @@ exp_do_unexport(svc_export *unexp)
{
svc_export *exp;
svc_client *clp;
+ struct dentry *dentry;
struct inode *inode;
int i;
@@ -247,11 +277,12 @@ exp_do_unexport(svc_export *unexp)
exp->ex_parent = unexp->ex_parent;
}
- inode = unexp->ex_inode;
+ dentry = unexp->ex_dentry;
+ inode = dentry->d_inode;
if (unexp->ex_dev != inode->i_dev || unexp->ex_ino != inode->i_ino)
- printk(KERN_WARNING "nfsd: bad inode in unexport!\n");
+ printk(KERN_WARNING "nfsd: bad dentry in unexport!\n");
else
- iput(unexp->ex_inode);
+ dput(dentry);
kfree(unexp);
}
@@ -326,13 +357,28 @@ exp_rootfh(struct svc_client *clp, dev_t dev, ino_t ino, struct knfs_fh *f)
{
struct svc_export *exp = NULL;
struct svc_fh fh;
+ struct dentry *dentry;
+ struct inode *inode;
- dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", clp->cl_ident, dev, ino);
+ dprintk("nfsd: exp_rootfh(%s:%x/%d)\n", clp->cl_ident, dev, ino);
if (!(exp = exp_get(clp, dev, ino)))
return -EPERM;
- exp->ex_inode->i_count++;
- fh_compose(&fh, exp, exp->ex_inode);
+
+ dentry = exp->ex_dentry;
+ inode = dentry->d_inode;
+ if(!inode) {
+ printk("exp_rootfh: Aieee, NULL d_inode\n");
+ return -EPERM;
+ }
+ if(inode->i_dev != dev || inode->i_ino != ino) {
+ printk("exp_rootfh: Aieee, ino/dev mismatch\n");
+ printk("exp_rootfh: arg[dev(%x):ino(%d)] inode[dev(%x):ino(%ld)]\n",
+ dev, ino, inode->i_dev, inode->i_ino);
+ }
+
+ dget(dentry);
+ fh_compose(&fh, exp, dentry);
memcpy(f, &fh.fh_handle, sizeof(struct knfs_fh));
fh_put(&fh);
@@ -613,25 +659,6 @@ exp_verify_string(char *cp, int max)
return 0;
}
-#if 0
-/*
- * Get the inode associated with a pathname. Used by exp_export.
- */
-struct inode *
-exp_lnamei(char *pathname, int *errp)
-{
- struct inode *inode;
- unsigned long oldfs;
-
- oldfs = get_fs();
- set_fs(KERNEL_DS);
- *errp = lnamei(pathname, &inode);
- set_fs(oldfs);
-
- return inode;
-}
-#endif
-
/*
* Initialize the exports module.
*/
@@ -648,7 +675,6 @@ nfsd_export_init(void)
clients = NULL;
initialized = 1;
- return;
}
/*
@@ -672,6 +698,4 @@ nfsd_export_shutdown(void)
}
exp_unlock();
dprintk("nfsd: export shutdown complete.\n");
-
- return;
}
diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c
index 3732ebcce..6e161d62b 100644
--- a/fs/nfsd/lockd.c
+++ b/fs/nfsd/lockd.c
@@ -24,7 +24,7 @@ nlm_fopen(struct svc_rqst *rqstp, struct knfs_fh *f, struct file *filp)
fh.fh_handle = *f;
fh.fh_export = NULL;
- fh.fh_inode = NULL;
+ fh.fh_dverified = 0;
nfserr = nfsd_open(rqstp, &fh, S_IFREG, 0, filp);
fh_put(&fh);
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index fb1c0878b..c748e118d 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -58,7 +58,7 @@ nfsd3_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp,
SVCFH_INO(&argp->fh));
resp->fh = argp->fh;
- nfserr = fh_lookup(rqstp, &resp->fh, 0, MAY_NOP);
+ nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
RETURN(nfserr);
}
@@ -227,7 +227,7 @@ nfsd3_proc_create(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
attr = &argp->attrs;
/* Get the directory inode */
- nfserr = fh_lookup(rqstp, dirfhp, S_IFDIR, MAY_CREATE);
+ nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_CREATE);
if (nfserr)
RETURN(nfserr);
@@ -241,8 +241,7 @@ nfsd3_proc_create(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
/* Now create the file and set attributes */
nfserr = nfsd_create(rqstp, dirfhp, argp->name, argp->len,
- attr, S_IFREG, 0, newfhp,
- argp->createmode);
+ attr, S_IFREG, 0, newfhp);
RETURN(nfserr);
}
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 88b69cb40..1db374bc6 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -133,9 +133,10 @@ nfsctl_getfh(struct nfsctl_fhparm *data, struct knfs_fh *res)
#endif
int
-asmlinkage handle_sys_nfsservctl(int cmd, struct nfsctl_arg *argp,
- union nfsctl_res *resp)
+asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
{
+ struct nfsctl_arg * argp = opaque_argp;
+ union nfsctl_res * resp = opaque_resp;
struct nfsctl_arg * arg = NULL;
union nfsctl_res * res = NULL;
int err;
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index 526a4455b..3ea0ee988 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -18,44 +18,23 @@
#define NFSDDBG_FACILITY NFSDDBG_FH
/*
- * Get the inode version number
- */
-static inline int
-nfsd_iversion(struct inode *inode)
-{
- if (inode->i_sb->s_magic == EXT2_SUPER_MAGIC)
- return inode->u.ext2_i.i_version;
- return 0;
-}
-
-/*
- * Get the inode given a file handle.
+ * Perform sanity checks on the dentry in a client's file handle.
*/
u32
-fh_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
+fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
{
struct svc_export *exp;
+ struct dentry *dentry;
struct inode *inode;
struct knfs_fh *fh = &fhp->fh_handle;
- /* Already checked */
- if (fhp->fh_inode)
+ if(fhp->fh_dverified)
return 0;
- dprintk("nfsd: fh_lookup(exp %x/%ld fh %x/%ld)\n",
- fh->fh_xdev, fh->fh_xino, fh->fh_dev, fh->fh_ino);
+ dprintk("nfsd: fh_lookup(exp %x/%d fh %p)\n",
+ fh->fh_xdev, fh->fh_xino, fh->fh_dentry);
- /* Make sure that clients don't cheat */
- if (fh->fh_dev != fh->fh_xdev) {
- printk(KERN_NOTICE "nfsd: fh with bad dev fields "
- "(%x != %x) from %08lx:%d\n",
- fh->fh_dev, fh->fh_xdev,
- ntohl(rqstp->rq_addr.sin_addr.s_addr),
- ntohs(rqstp->rq_addr.sin_port));
- return nfserr_perm;
- }
-
- /* Look up the export entry */
+ /* Look up the export entry. */
exp = exp_get(rqstp->rq_client, fh->fh_xdev, fh->fh_xino);
if (!exp) {
/* nfsdstats.fhstale++; */
@@ -71,22 +50,26 @@ fh_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
return nfserr_perm;
}
- /* Set user creds if we haven't done so already */
+ /* Set user creds if we haven't done so already. */
nfsd_setuser(rqstp, exp);
- /* Get the inode */
- if (!(inode = nfsd_iget(fh->fh_dev, fh->fh_ino))
- || !inode->i_nlink || fh->fh_version != nfsd_iversion(inode)) {
- if (inode)
- iput(inode);
+ dentry = fh->fh_dentry;
+
+ if(!d_validate(dentry, fh->fh_dparent, fh->fh_dhash, fh->fh_dlen) ||
+ !(inode = dentry->d_inode) ||
+ !inode->i_nlink) {
+ /* Currently we cannot tell the difference between
+ * a bogus pointer and a true unlink between fh
+ * uses. But who cares about accurate error reporting
+ * to buggy/malicious clients... -DaveM
+ */
+
/* nfsdstats.fhstale++; */
- return nfserr_stale; /* unlinked in the meanwhile */
+ return nfserr_stale;
}
- /* This is basically what wait_on_inode does */
- while (inode->i_lock)
- sleep_on(&inode->i_wait);
- fhp->fh_inode = inode;
+ dget(dentry);
+ fhp->fh_dverified = 1;
fhp->fh_export = exp;
/* Type check. The correct error return for type mismatches
@@ -100,25 +83,28 @@ fh_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
if (type < 0 && (inode->i_mode & S_IFMT) == -type)
return (type == -S_IFDIR)? nfserr_notdir : nfserr_isdir;
- /* Finally, check access permissions */
- return nfsd_permission(fhp->fh_export, inode, access);
+ /* Finally, check access permissions. */
+ return nfsd_permission(fhp->fh_export, dentry, access);
}
/*
* Compose file handle for NFS reply.
*/
void
-fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct inode *inode)
+fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry)
{
- dprintk("nfsd: fh_compose(exp %x/%ld fh %x/%ld)\n",
- exp->ex_dev, exp->ex_ino, inode->i_dev, inode->i_ino);
+ dprintk("nfsd: fh_compose(exp %x/%d dentry %p)\n",
+ exp->ex_dev, exp->ex_ino, dentry);
fh_init(fhp); /* initialize empty fh */
- fhp->fh_inode = inode;
- fhp->fh_export = exp;
- fhp->fh_handle.fh_dev = inode->i_dev;
- fhp->fh_handle.fh_ino = inode->i_ino;
+ fhp->fh_handle.fh_dentry = dentry;
+ fhp->fh_handle.fh_dparent = dentry->d_parent;
+ fhp->fh_handle.fh_dhash = dentry->d_name.hash;
+ fhp->fh_handle.fh_dlen = dentry->d_name.len;
fhp->fh_handle.fh_xdev = exp->ex_dev;
fhp->fh_handle.fh_xino = exp->ex_ino;
- fhp->fh_handle.fh_version = nfsd_iversion(inode);
+ fhp->fh_export = exp;
+
+ /* We stuck it there, we know it's good. */
+ fhp->fh_dverified = 1;
}
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index 05b13deab..5b6e6cdf1 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -55,12 +55,10 @@ static int
nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp,
struct nfsd_attrstat *resp)
{
- dprintk("nfsd: GETATTR %x/%ld\n",
- SVCFH_DEV(&argp->fh),
- SVCFH_INO(&argp->fh));
+ dprintk("nfsd: GETATTR %p\n", SVCFH_DENTRY(&argp->fh));
fh_copy(&resp->fh, &argp->fh);
- RETURN(fh_lookup(rqstp, &resp->fh, 0, MAY_NOP));
+ RETURN(fh_verify(rqstp, &resp->fh, 0, MAY_NOP));
}
/*
@@ -70,9 +68,7 @@ static int
nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp,
struct nfsd_attrstat *resp)
{
- dprintk("nfsd: SETATTR %x/%ld\n",
- SVCFH_DEV(&argp->fh),
- SVCFH_INO(&argp->fh));
+ dprintk("nfsd: SETATTR %p\n", SVCFH_DENTRY(&argp->fh));
fh_copy(&resp->fh, &argp->fh);
RETURN(nfsd_setattr(rqstp, &resp->fh, &argp->attrs));
@@ -87,10 +83,7 @@ nfsd_proc_lookup(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
{
int nfserr;
- dprintk("nfsd: LOOKUP %x/%ld %s\n",
- SVCFH_DEV(&argp->fh),
- SVCFH_INO(&argp->fh),
- argp->name);
+ dprintk("nfsd: LOOKUP %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
nfserr = nfsd_lookup(rqstp, &argp->fh,
argp->name,
@@ -111,9 +104,7 @@ nfsd_proc_readlink(struct svc_rqst *rqstp, struct nfsd_fhandle *argp,
u32 *path;
int dummy, nfserr;
- dprintk("nfsd: READLINK %x/%ld\n",
- SVCFH_DEV(&argp->fh),
- SVCFH_INO(&argp->fh));
+ dprintk("nfsd: READLINK %p\n", SVCFH_DENTRY(&argp->fh));
/* Reserve room for status and path length */
svcbuf_reserve(&rqstp->rq_resbuf, &path, &dummy, 2);
@@ -136,10 +127,9 @@ nfsd_proc_read(struct svc_rqst *rqstp, struct nfsd_readargs *argp,
u32 * buffer;
int nfserr, avail;
- dprintk("nfsd: READ %x/%ld %d bytes at %d\n",
- SVCFH_DEV(&argp->fh),
- SVCFH_INO(&argp->fh),
- argp->count, argp->offset);
+ dprintk("nfsd: READ %p %d bytes at %d\n",
+ SVCFH_DENTRY(&argp->fh),
+ argp->count, argp->offset);
/* Obtain buffer pointer for payload. 19 is 1 word for
* status, 17 words for fattr, and 1 word for the byte count.
@@ -173,10 +163,9 @@ nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp,
{
int nfserr;
- dprintk("nfsd: WRITE %x/%ld %d bytes at %d\n",
- SVCFH_DEV(&argp->fh),
- SVCFH_INO(&argp->fh),
- argp->len, argp->offset);
+ dprintk("nfsd: WRITE %p %d bytes at %d\n",
+ SVCFH_DENTRY(&argp->fh),
+ argp->len, argp->offset);
nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh),
argp->offset,
@@ -204,23 +193,22 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
int rdonly = 0, exists;
dev_t rdev = NODEV;
- dprintk("nfsd: CREATE %x/%ld %s\n",
- SVCFH_DEV(&argp->fh),
- SVCFH_INO(&argp->fh),
- argp->name);
+ dprintk("nfsd: CREATE %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
dirfhp = &argp->fh;
newfhp = &resp->fh;
attr = &argp->attrs;
/* Get the directory inode */
- nfserr = fh_lookup(rqstp, dirfhp, S_IFDIR, MAY_EXEC);
+ nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_EXEC);
if (nfserr)
RETURN(nfserr);
- dirp = dirfhp->fh_inode;
+ dirp = dirfhp->fh_handle.fh_dentry->d_inode;
/* Check for MAY_WRITE separately. */
- nfserr = nfsd_permission(dirfhp->fh_export, dirp, MAY_WRITE);
+ nfserr = nfsd_permission(dirfhp->fh_export,
+ dirfhp->fh_handle.fh_dentry,
+ MAY_WRITE);
if (nfserr == nfserr_rofs) {
rdonly = 1; /* Non-fatal error for echo > /dev/null */
} else if (nfserr) {
@@ -230,7 +218,16 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
/* First, check if the file already exists. */
exists = !nfsd_lookup(rqstp, dirfhp, argp->name, argp->len, newfhp);
- inode = newfhp->fh_inode;
+
+ if (newfhp->fh_dverified)
+ inode = newfhp->fh_handle.fh_dentry->d_inode;
+
+ /* Get rid of this soon... */
+ if (exists && !inode) {
+ printk("nfsd_proc_create: Wheee... exists but d_inode==NULL\n");
+ nfserr = nfserr_rofs;
+ goto done;
+ }
/* Unfudge the mode bits */
if (attr->ia_valid & ATTR_MODE) {
@@ -311,10 +308,7 @@ nfsd_proc_remove(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
{
int nfserr;
- dprintk("nfsd: REMOVE %x/%ld %s\n",
- SVCFH_DEV(&argp->fh),
- SVCFH_INO(&argp->fh),
- argp->name);
+ dprintk("nfsd: REMOVE %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
/* Unlink. -SIFDIR means file must not be a directory */
nfserr = nfsd_unlink(rqstp, &argp->fh, -S_IFDIR, argp->name, argp->len);
@@ -328,13 +322,9 @@ nfsd_proc_rename(struct svc_rqst *rqstp, struct nfsd_renameargs *argp,
{
int nfserr;
- dprintk("nfsd: RENAME %x/%ld %s -> %x/%ld %s\n",
- SVCFH_DEV(&argp->ffh),
- SVCFH_INO(&argp->ffh),
- argp->fname,
- SVCFH_DEV(&argp->tfh),
- SVCFH_INO(&argp->tfh),
- argp->tname);
+ dprintk("nfsd: RENAME %p %s -> %p %s\n",
+ SVCFH_DENTRY(&argp->ffh), argp->fname,
+ SVCFH_DENTRY(&argp->tfh), argp->tname);
nfserr = nfsd_rename(rqstp, &argp->ffh, argp->fname, argp->flen,
&argp->tfh, argp->tname, argp->tlen);
@@ -349,12 +339,10 @@ nfsd_proc_link(struct svc_rqst *rqstp, struct nfsd_linkargs *argp,
{
int nfserr;
- dprintk("nfsd: LINK %x/%ld -> %x/%ld %s\n",
- SVCFH_DEV(&argp->ffh),
- SVCFH_INO(&argp->ffh),
- SVCFH_DEV(&argp->tfh),
- SVCFH_INO(&argp->tfh),
- argp->tname);
+ dprintk("nfsd: LINK %p -> %p %s\n",
+ SVCFH_DENTRY(&argp->ffh),
+ SVCFH_DENTRY(&argp->tfh),
+ argp->tname);
nfserr = nfsd_link(rqstp, &argp->tfh, argp->tname, argp->tlen,
&argp->ffh);
@@ -370,10 +358,9 @@ nfsd_proc_symlink(struct svc_rqst *rqstp, struct nfsd_symlinkargs *argp,
struct svc_fh newfh;
int nfserr;
- dprintk("nfsd: SYMLINK %x/%ld %s -> %s\n",
- SVCFH_DEV(&argp->ffh),
- SVCFH_INO(&argp->ffh),
- argp->fname, argp->tname);
+ dprintk("nfsd: SYMLINK %p %s -> %s\n",
+ SVCFH_DENTRY(&argp->ffh),
+ argp->fname, argp->tname);
/*
* Create the link, look up new file and set attrs.
@@ -381,7 +368,7 @@ nfsd_proc_symlink(struct svc_rqst *rqstp, struct nfsd_symlinkargs *argp,
nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
argp->tname, argp->tlen,
&newfh);
- if (nfserr)
+ if (!nfserr)
nfserr = nfsd_setattr(rqstp, &newfh, &argp->attrs);
fh_put(&argp->ffh);
@@ -398,11 +385,11 @@ nfsd_proc_mkdir(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
{
int nfserr;
- dprintk("nfsd: MKDIR %x/%ld %s\n",
- SVCFH_DEV(&argp->fh),
- SVCFH_INO(&argp->fh),
- argp->name);
+ dprintk("nfsd: MKDIR %p %s\n",
+ SVCFH_DENTRY(&argp->fh),
+ argp->name);
+ resp->fh.fh_dverified = 0; /* paranoia */
nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len,
&argp->attrs, S_IFDIR, 0, &resp->fh);
fh_put(&argp->fh);
@@ -418,10 +405,7 @@ nfsd_proc_rmdir(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
{
int nfserr;
- dprintk("nfsd: RMDIR %x/%ld %s\n",
- SVCFH_DEV(&argp->fh),
- SVCFH_INO(&argp->fh),
- argp->name);
+ dprintk("nfsd: RMDIR %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
nfserr = nfsd_unlink(rqstp, &argp->fh, S_IFDIR, argp->name, argp->len);
fh_put(&argp->fh);
@@ -438,10 +422,9 @@ nfsd_proc_readdir(struct svc_rqst *rqstp, struct nfsd_readdirargs *argp,
u32 * buffer;
int nfserr, count;
- dprintk("nfsd: READDIR %x/%ld %d bytes at %d\n",
- SVCFH_DEV(&argp->fh),
- SVCFH_INO(&argp->fh),
- argp->count, argp->cookie);
+ dprintk("nfsd: READDIR %p %d bytes at %d\n",
+ SVCFH_DENTRY(&argp->fh),
+ argp->count, argp->cookie);
/* Reserve buffer space for status */
svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count, 1);
@@ -470,9 +453,7 @@ nfsd_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle *argp,
{
int nfserr;
- dprintk("nfsd: STATFS %x/%ld\n",
- SVCFH_DEV(&argp->fh),
- SVCFH_INO(&argp->fh));
+ dprintk("nfsd: STATFS %p\n", SVCFH_DENTRY(&argp->fh));
nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats);
fh_put(&argp->fh);
@@ -488,34 +469,34 @@ nfsd_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle *argp,
struct nfsd_void { int dummy; };
#define PROC(name, argt, rest, relt, cache) \
- { (svc_procfunc) nfsd_proc_##name, \
- (kxdrproc_t) nfssvc_decode_##argt, \
- (kxdrproc_t) nfssvc_encode_##rest, \
- (kxdrproc_t) nfssvc_release_##relt, \
- sizeof(struct nfsd_##argt), \
- sizeof(struct nfsd_##rest), \
- 0, \
- cache \
+ { (svc_procfunc) nfsd_proc_##name, \
+ (kxdrproc_t) nfssvc_decode_##argt, \
+ (kxdrproc_t) nfssvc_encode_##rest, \
+ (kxdrproc_t) nfssvc_release_##relt, \
+ sizeof(struct nfsd_##argt), \
+ sizeof(struct nfsd_##rest), \
+ 0, \
+ cache \
}
struct svc_procedure nfsd_procedures2[18] = {
- PROC(null, void, void, none, RC_NOCACHE),
- PROC(getattr, fhandle, attrstat, fhandle, RC_NOCACHE),
- PROC(setattr, sattrargs, attrstat, fhandle, RC_REPLBUFF),
- PROC(none, void, void, none, RC_NOCACHE),
- PROC(lookup, diropargs, diropres, fhandle, RC_NOCACHE),
- PROC(readlink, fhandle, readlinkres, none, RC_NOCACHE),
- PROC(read, readargs, readres, fhandle, RC_NOCACHE),
- PROC(none, void, void, none, RC_NOCACHE),
- PROC(write, writeargs, attrstat, fhandle, RC_REPLBUFF),
- PROC(create, createargs, diropres, fhandle, RC_REPLBUFF),
- PROC(remove, diropargs, void, none, RC_REPLSTAT),
- PROC(rename, renameargs, void, none, RC_REPLSTAT),
- PROC(link, linkargs, void, none, RC_REPLSTAT),
- PROC(symlink, symlinkargs, void, none, RC_REPLSTAT),
- PROC(mkdir, createargs, diropres, fhandle, RC_REPLBUFF),
- PROC(rmdir, diropargs, void, none, RC_REPLSTAT),
- PROC(readdir, readdirargs, readdirres, none, RC_REPLSTAT),
- PROC(statfs, fhandle, statfsres, none, RC_NOCACHE),
+ PROC(null, void, void, none, RC_NOCACHE),
+ PROC(getattr, fhandle, attrstat, fhandle, RC_NOCACHE),
+ PROC(setattr, sattrargs, attrstat, fhandle, RC_REPLBUFF),
+ PROC(none, void, void, none, RC_NOCACHE),
+ PROC(lookup, diropargs, diropres, fhandle, RC_NOCACHE),
+ PROC(readlink, fhandle, readlinkres, none, RC_NOCACHE),
+ PROC(read, readargs, readres, fhandle, RC_NOCACHE),
+ PROC(none, void, void, none, RC_NOCACHE),
+ PROC(write, writeargs, attrstat, fhandle, RC_REPLBUFF),
+ PROC(create, createargs, diropres, fhandle, RC_REPLBUFF),
+ PROC(remove, diropargs, void, none, RC_REPLSTAT),
+ PROC(rename, renameargs, void, none, RC_REPLSTAT),
+ PROC(link, linkargs, void, none, RC_REPLSTAT),
+ PROC(symlink, symlinkargs, void, none, RC_REPLSTAT),
+ PROC(mkdir, createargs, diropres, fhandle, RC_REPLBUFF),
+ PROC(rmdir, diropargs, void, none, RC_REPLSTAT),
+ PROC(readdir, readdirargs, readdirres, none, RC_REPLSTAT),
+ PROC(statfs, fhandle, statfsres, none, RC_NOCACHE),
};
diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c
index 34b9ff3f2..cec3ba7f3 100644
--- a/fs/nfsd/nfsxdr.c
+++ b/fs/nfsd/nfsxdr.c
@@ -363,7 +363,7 @@ int
nfssvc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
struct nfsd_attrstat *resp)
{
- if (!(p = encode_fattr(rqstp, p, resp->fh.fh_inode)))
+ if (!(p = encode_fattr(rqstp, p, resp->fh.fh_handle.fh_dentry->d_inode)))
return 0;
return xdr_ressize_check(rqstp, p);
}
@@ -373,7 +373,7 @@ nfssvc_encode_diropres(struct svc_rqst *rqstp, u32 *p,
struct nfsd_diropres *resp)
{
if (!(p = encode_fh(p, &resp->fh))
- || !(p = encode_fattr(rqstp, p, resp->fh.fh_inode)))
+ || !(p = encode_fattr(rqstp, p, resp->fh.fh_handle.fh_dentry->d_inode)))
return 0;
return xdr_ressize_check(rqstp, p);
}
@@ -391,7 +391,7 @@ int
nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p,
struct nfsd_readres *resp)
{
- if (!(p = encode_fattr(rqstp, p, resp->fh.fh_inode)))
+ if (!(p = encode_fattr(rqstp, p, resp->fh.fh_handle.fh_dentry->d_inode)))
return 0;
*p++ = htonl(resp->count);
p += XDR_QUADLEN(resp->count);
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index b6fdb460d..4fa6c08fb 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -5,12 +5,11 @@
* other parts of the kernel because they weren't in ksyms.c, others
* are partial duplicates with added or changed functionality.
*
- * Note that several functions lock the inode upon which they want
- * to act, most notably those that create directory entries. The
- * unlock operation can take place either by calling fh_unlock within
- * the function directly, or at a later time in fh_put(). So if you
- * notice code paths that apparently fail to unlock the inode, don't
- * worry--they have been taken care of.
+ * Note that several functions dget() the dentry upon which they want
+ * to act, most notably those that create directory entries. Response
+ * dentry's are dput()'d if necessary in the release callback.
+ * So if you notice code paths that apparently fail to dput() the
+ * dentry, don't worry--they have been taken care of.
*
* Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
*/
@@ -59,10 +58,9 @@
* add a hash table here.
*/
struct raparms {
- struct raparms * p_next;
+ struct raparms *p_next;
unsigned int p_count;
- dev_t p_dev;
- ino_t p_ino;
+ struct dentry *p_dentry;
unsigned long p_reada,
p_ramax,
p_raend,
@@ -85,12 +83,14 @@ fs_off_limits(struct super_block *sb)
}
/*
- * Check whether directory is a mount point
+ * Check whether directory is a mount point, but it is alright if
+ * this is precisely the local mount point being exported.
*/
static inline int
-nfsd_iscovered(struct inode *inode)
+nfsd_iscovered(struct dentry *dentry, struct svc_export *exp)
{
- return inode->i_mount != NULL;
+ return (dentry != dentry->d_covers &&
+ dentry != exp->ex_dentry);
}
/*
@@ -101,72 +101,40 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
int len, struct svc_fh *resfh)
{
struct svc_export *exp;
- struct super_block *sb;
- struct inode *dirp, *inode;
- int perm, err, dotdot = 0;
+ struct dentry *dparent;
+ int err;
- dprintk("nfsd: nfsd_lookup(fh %x/%ld, %s)\n",
- SVCFH_DEV(fhp), SVCFH_INO(fhp), name);
+ dprintk("nfsd: nfsd_lookup(fh %p, %s)\n", SVCFH_DENTRY(fhp), name);
- /* Obtain inode and export */
- if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_NOP)) != 0)
+ /* Obtain dentry and export. */
+ if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_NOP)) != 0)
return err;
- dirp = fhp->fh_inode;
- exp = fhp->fh_export;
- /* check permissions before traversing mount-points */
- perm = nfsd_permission(exp, dirp, MAY_EXEC);
-
- dotdot = (len == 2 && name[0] == '.' && name[1] == '.');
- if (dotdot) {
- if (dirp == current->fs->root) {
- dirp->i_count++;
- *resfh = *fhp;
- return 0;
- }
-
- if (dirp->i_dev == exp->ex_dev && dirp->i_ino == exp->ex_ino) {
- dirp->i_count++;
- *resfh = *fhp;
- return 0;
- }
- } else if (len == 1 && name[0] == '.') {
- len = 0;
- } else if (fs_off_limits(dirp->i_sb)) {
- /* No lookups on NFS mounts and procfs */
- return nfserr_noent;
- } else if (nfsd_iscovered(dirp)) {
- /* broken NFS client */
- return nfserr_acces;
- }
- if (!dirp->i_op || !dirp->i_op->lookup)
- return nfserr_notdir;
- if (perm != 0)
- return perm;
- if (!len) {
- dirp->i_count++;
- *resfh = *fhp;
- return 0;
- }
+ dparent = fhp->fh_handle.fh_dentry;
+ exp = fhp->fh_export;
- dirp->i_count++; /* lookup eats the dirp inode */
- err = dirp->i_op->lookup(dirp, name, len, &inode);
+ /* Fast path... */
+ err = nfsd_permission(exp, dparent, MAY_EXEC);
+ if ((err == 0) &&
+ !fs_off_limits(dparent->d_inode->i_sb) &&
+ !nfsd_iscovered(dparent, exp)) {
+ struct dentry *dchild;
- if (err)
- return nfserrno(-err);
+ dchild = lookup_dentry(name, dget(dparent), 1);
+ err = PTR_ERR(dchild);
+ if (IS_ERR(dchild))
+ return nfserrno(-err);
- /* Note that lookup() has already done a call to iget() so that
- * the inode returned never refers to an inode covered by a mount.
- * When this has happened, return the covered inode.
- */
- if (!dotdot && (sb = inode->i_sb) && (inode == sb->s_mounted)) {
- iput(inode);
- inode = sb->s_covered;
- inode->i_count++;
+ fh_compose(resfh, exp, dchild);
+ return (dchild->d_inode ? 0 : nfserr_noent);
}
- fh_compose(resfh, exp, inode);
- return 0;
+ /* Slow path... */
+ if (fs_off_limits(dparent->d_inode->i_sb))
+ return nfserr_noent;
+ if (nfsd_iscovered(dparent, exp))
+ return nfserr_acces;
+ return err;
}
/*
@@ -175,6 +143,7 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
int
nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
{
+ struct dentry *dentry;
struct inode *inode;
int accmode = MAY_SATTR;
int ftype = 0;
@@ -187,16 +156,16 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
ftype = S_IFREG;
/* Get inode */
- if ((err = fh_lookup(rqstp, fhp, ftype, accmode)) != 0)
+ if ((err = fh_verify(rqstp, fhp, ftype, accmode)) != 0)
return err;
- fh_lock(fhp); /* lock inode */
- inode = fhp->fh_inode;
+ dentry = fhp->fh_handle.fh_dentry;
+ inode = dentry->d_inode;
/* The size case is special... */
if ((iap->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode)) {
if (iap->ia_size < inode->i_size) {
- err = nfsd_permission(fhp->fh_export, inode, MAY_TRUNC);
+ err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC);
if (err != 0)
return err;
}
@@ -205,7 +174,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
inode->i_size = iap->ia_size;
if (inode->i_op && inode->i_op->truncate)
inode->i_op->truncate(inode);
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
put_write_access(inode);
iap->ia_valid &= ATTR_SIZE;
iap->ia_valid |= ATTR_MTIME;
@@ -234,11 +203,11 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
if (iap->ia_valid) {
iap->ia_valid |= ATTR_CTIME;
iap->ia_ctime = CURRENT_TIME;
- err = nfsd_notify_change(inode, iap);
+ err = notify_change(inode, iap);
if (err)
return nfserrno(-err);
if (EX_ISSYNC(fhp->fh_export))
- nfsd_write_inode(inode);
+ write_inode_now(inode);
}
return 0;
@@ -252,13 +221,16 @@ int
nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
int wflag, struct file *filp)
{
+ struct dentry *dentry;
struct inode *inode;
int access, err;
access = wflag? MAY_WRITE : MAY_READ;
- if ((err = fh_lookup(rqstp, fhp, type, access)) != 0)
+ if ((err = fh_verify(rqstp, fhp, type, access)) != 0)
return err;
- inode = fhp->fh_inode;
+
+ dentry = fhp->fh_handle.fh_dentry;
+ inode = dentry->d_inode;
/* Disallow access to files with the append-only bit set or
* with mandatory locking enabled */
@@ -275,7 +247,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
filp->f_count = 1;
filp->f_flags = wflag? O_WRONLY : O_RDONLY;
filp->f_mode = wflag? FMODE_WRITE : FMODE_READ;
- filp->f_inode = inode;
+ filp->f_dentry = dentry;
if (filp->f_op->open) {
err = filp->f_op->open(inode, filp);
@@ -291,7 +263,6 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
}
}
- inode->i_count++;
return 0;
}
@@ -301,16 +272,17 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
void
nfsd_close(struct file *filp)
{
- struct inode *inode;
+ struct dentry *dentry = filp->f_dentry;
+ struct inode *inode = dentry->d_inode;
- inode = filp->f_inode;
if (!inode->i_count)
printk(KERN_WARNING "nfsd: inode count == 0!\n");
+ if (!dentry->d_count)
+ printk(KERN_WARNING "nfsd: wheee, dentry count == 0!\n");
if (filp->f_op && filp->f_op->release)
filp->f_op->release(inode, filp);
if (filp->f_mode & FMODE_WRITE)
put_write_access(inode);
- iput(inode);
}
/*
@@ -326,12 +298,12 @@ nfsd_sync(struct inode *inode, struct file *filp)
* Obtain the readahead parameters for the given file
*/
static inline struct raparms *
-nfsd_get_raparms(dev_t dev, ino_t ino)
+nfsd_get_raparms(struct dentry *dentry)
{
struct raparms *ra, **rap, **frap = NULL;
for (rap = &raparm_cache; (ra = *rap); rap = &ra->p_next) {
- if (ra->p_dev != dev || ra->p_ino != ino) {
+ if (ra->p_dentry != dentry) {
if (ra->p_count == 0)
frap = rap;
} else
@@ -342,6 +314,7 @@ nfsd_get_raparms(dev_t dev, ino_t ino)
rap = frap;
ra = *frap;
memset(ra, 0, sizeof(*ra));
+ ra->p_dentry = dentry;
found:
if (rap != &raparm_cache) {
*rap = ra->p_next;
@@ -361,6 +334,7 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, char *buf,
unsigned long *count)
{
struct raparms *ra;
+ struct dentry *dentry;
struct inode *inode;
struct file file;
unsigned long oldfs;
@@ -368,14 +342,15 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, char *buf,
if ((err = nfsd_open(rqstp, fhp, S_IFREG, OPEN_READ, &file)) != 0)
return err;
- inode = file.f_inode;
+ dentry = file.f_dentry;
+ inode = dentry->d_inode;
if (!file.f_op->read) {
nfsd_close(&file);
return nfserr_perm;
}
/* Get readahead parameters */
- if ((ra = nfsd_get_raparms(inode->i_dev, inode->i_ino)) != NULL) {
+ if ((ra = nfsd_get_raparms(dentry)) != NULL) {
file.f_reada = ra->p_reada;
file.f_ramax = ra->p_ramax;
file.f_raend = ra->p_raend;
@@ -385,7 +360,7 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, char *buf,
file.f_pos = offset;
oldfs = get_fs(); set_fs(KERNEL_DS);
- err = file.f_op->read(file.f_inode, &file, buf, *count);
+ err = file.f_op->read(inode, &file, buf, *count);
set_fs(oldfs);
/* Write back readahead params */
@@ -419,6 +394,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
{
struct svc_export *exp;
struct file file;
+ struct dentry *dentry;
struct inode *inode;
unsigned long oldfs;
int err;
@@ -432,7 +408,8 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
return nfserr_perm;
}
- inode = fhp->fh_inode;
+ dentry = file.f_dentry;
+ inode = dentry->d_inode;
exp = fhp->fh_export;
/*
@@ -459,7 +436,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
ia.ia_valid = ATTR_MODE;
ia.ia_mode = inode->i_mode & ~(S_ISUID | S_ISGID);
- nfsd_notify_change(inode, &ia);
+ notify_change(inode, &ia);
}
fh_unlock(fhp); /* unlock inode */
@@ -494,10 +471,10 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
#endif
}
- if (inode->i_dirt) {
+ if (test_bit(I_DIRTY, &inode->i_state)) {
dprintk("nfsd: write sync %d\n", current->pid);
nfsd_sync(inode, &file);
- nfsd_write_inode(inode);
+ write_inode_now(inode);
}
wake_up(&inode->i_wait);
last_ino = inode->i_ino;
@@ -519,110 +496,120 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
char *fname, int flen, struct iattr *iap,
int type, dev_t rdev, struct svc_fh *resfhp)
{
- struct inode *dirp, *inode = NULL;
+ struct dentry *dentry, *dchild;
+ struct inode *dirp;
int err;
if (!flen)
return nfserr_perm;
-
if (!(iap->ia_valid & ATTR_MODE))
iap->ia_mode = 0;
-
- if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0)
+ if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0)
return err;
- fh_lock(fhp); /* lock directory */
- dirp = fhp->fh_inode;
- dirp->i_count++; /* dirop eats the inode */
+ dentry = fhp->fh_handle.fh_dentry;
+ dirp = dentry->d_inode;
+
+ /* Get all the sanity checks out of the way before we lock the parent. */
+ if(!dirp->i_op || !dirp->i_op->lookup) {
+ return nfserrno(-ENOTDIR);
+ } else if(type == S_IFREG) {
+ if(!dirp->i_op->create)
+ return nfserr_perm;
+ } else if(type == S_IFDIR) {
+ if(!dirp->i_op->mkdir)
+ return nfserr_perm;
+ } else if((type == S_IFCHR) || (type == S_IFBLK) || (type == S_IFIFO)) {
+ if(!dirp->i_op->mknod)
+ return nfserr_perm;
+ } else {
+ return nfserr_perm;
+ }
+ if(!resfhp->fh_dverified) {
+ dchild = lookup_dentry(fname, dget(dentry), 0);
+ err = PTR_ERR(dchild);
+ if(IS_ERR(dchild))
+ return nfserrno(-err);
+ } else
+ dchild = resfhp->fh_handle.fh_dentry;
+
+ /* Looks good, lock the directory. */
+ fh_lock(fhp);
switch (type) {
case S_IFREG:
- if (!dirp->i_op || !dirp->i_op->create)
- return nfserr_perm;
- err = dirp->i_op->create(dirp, fname, flen,
- iap->ia_mode, &inode);
+ err = dirp->i_op->create(dirp, dchild, iap->ia_mode);
break;
case S_IFDIR:
- if (!dirp->i_op || !dirp->i_op->mkdir)
- return nfserr_perm;
- err = dirp->i_op->mkdir(dirp, fname, flen, iap->ia_mode);
+ err = dirp->i_op->mkdir(dirp, dchild, iap->ia_mode);
break;
case S_IFCHR:
case S_IFBLK:
case S_IFIFO:
- if (!dirp->i_op || !dirp->i_op->mknod)
- return nfserr_perm;
- err = dirp->i_op->mknod(dirp, fname, flen, iap->ia_mode, rdev);
+ err = dirp->i_op->mknod(dirp, dchild, iap->ia_mode, rdev);
break;
- default:
- iput(dirp);
- err = -EACCES;
}
-
fh_unlock(fhp);
if (err < 0)
return nfserrno(-err);
-
- /*
- * If the VFS call doesn't return the inode, look it up now.
- */
- if (inode == NULL) {
- dirp->i_count++;
- err = dirp->i_op->lookup(dirp, fname, flen, &inode);
- if (err < 0)
- return -nfserrno(err); /* Huh?! */
- }
-
if (EX_ISSYNC(fhp->fh_export))
- nfsd_write_inode(dirp);
+ write_inode_now(dirp);
- /* Assemble the file handle for the newly created file */
- fh_compose(resfhp, fhp->fh_export, inode);
+ /* If needed, assemble the file handle for the newly created file. */
+ if(!resfhp->fh_dverified)
+ fh_compose(resfhp, fhp->fh_export, dchild);
/* Set file attributes. Mode has already been set and
* setting uid/gid works only for root. Irix appears to
* send along the gid when it tries to implement setgid
* directories via NFS.
*/
- if ((iap->ia_valid &= (ATTR_UID|ATTR_GID|ATTR_MODE)) != 0) {
- if ((err = nfsd_setattr(rqstp, resfhp, iap)) != 0) {
- fh_put(resfhp);
- return err;
- }
- }
+ err = 0;
+ if ((iap->ia_valid &= (ATTR_UID|ATTR_GID|ATTR_MODE)) != 0)
+ err = nfsd_setattr(rqstp, resfhp, iap);
- return 0;
+ return err;
}
/*
* Truncate a file.
* The calling routines must make sure to update the ctime
* field and call notify_change.
+ *
+ * XXX Nobody calls this thing? -DaveM
*/
int
nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size)
{
+ struct dentry *dentry;
struct inode *inode;
+ struct iattr newattrs;
int err;
- if ((err = fh_lookup(rqstp, fhp, S_IFREG, MAY_WRITE|MAY_TRUNC)) != 0)
+ if ((err = fh_verify(rqstp, fhp, S_IFREG, MAY_WRITE|MAY_TRUNC)) != 0)
return err;
- fh_lock(fhp); /* lock inode if not yet locked */
- inode = fhp->fh_inode;
+ dentry = fhp->fh_handle.fh_dentry;
+ inode = dentry->d_inode;
if ((err = get_write_access(inode)) != 0)
- return nfserrno(-err);
- inode->i_size = size;
- if (inode->i_op && inode->i_op->truncate)
- inode->i_op->truncate(inode);
- inode->i_dirt = 1;
+ goto out;
+
+ /* Things look sane, lock and do it. */
+ fh_lock(fhp);
+ newattrs.ia_size = size;
+ newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
+ err = notify_change(inode, &newattrs);
+ if (!err) {
+ vmtruncate(inode, size);
+ if (inode->i_op && inode->i_op->truncate)
+ inode->i_op->truncate(inode);
+ }
put_write_access(inode);
-
fh_unlock(fhp);
-
- return 0;
+out:
+ return (err ? nfserrno(-err) : 0);
}
/*
@@ -632,18 +619,21 @@ nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size)
int
nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp)
{
+ struct dentry *dentry;
struct inode *inode;
unsigned long oldfs;
int err;
- if ((err = fh_lookup(rqstp, fhp, S_IFLNK, MAY_READ)) != 0)
+ if ((err = fh_verify(rqstp, fhp, S_IFLNK, MAY_READ)) != 0)
return err;
- inode = fhp->fh_inode;
+
+ dentry = fhp->fh_handle.fh_dentry;
+ inode = dentry->d_inode;
if (!inode->i_op || !inode->i_op->readlink)
return nfserr_io;
- inode->i_count++;
+ UPDATE_ATIME(inode);
oldfs = get_fs(); set_fs(KERNEL_DS);
err = inode->i_op->readlink(inode, buf, *lenp);
set_fs(oldfs);
@@ -664,42 +654,45 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
char *path, int plen,
struct svc_fh *resfhp)
{
- struct inode *dirp, *inode;
+ struct dentry *dentry, *dnew;
+ struct inode *dirp;
int err;
if (!flen || !plen)
return nfserr_noent;
- if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0)
+ if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0)
return err;
- dirp = fhp->fh_inode;
- if (nfsd_iscovered(dirp))
- return nfserr_perm;
- if (!dirp->i_op || !dirp->i_op->symlink)
- return nfserr_perm;
+ dentry = fhp->fh_handle.fh_dentry;
+ dirp = dentry->d_inode;
- fh_lock(fhp); /* lock inode */
- dirp->i_count++;
- err = dirp->i_op->symlink(dirp, fname, flen, path);
- fh_unlock(fhp); /* unlock inode */
+ if (nfsd_iscovered(dentry, fhp->fh_export) ||
+ !dirp->i_op ||
+ !dirp->i_op->symlink)
+ return nfserr_perm;
- if (err)
- return nfserrno(-err);
+ dnew = lookup_dentry(fname, dget(dentry), 0);
+ err = PTR_ERR(dnew);
+ if (IS_ERR(dnew))
+ goto out;
- if (EX_ISSYNC(fhp->fh_export))
- nfsd_write_inode(dirp);
+ err = -EEXIST;
+ if(dnew->d_inode)
+ goto compose_and_out;
- /*
- * Okay, now look up the inode of the new symlink.
- */
- dirp->i_count++; /* lookup eats the dirp inode */
- err = dirp->i_op->lookup(dirp, fname, flen, &inode);
- if (err)
- return nfserrno(-err);
+ fh_lock(fhp);
+ err = dirp->i_op->symlink(dirp, dnew, path);
+ fh_unlock(fhp);
- fh_compose(resfhp, fhp->fh_export, inode);
- return 0;
+ if (!err) {
+ if (EX_ISSYNC(fhp->fh_export))
+ write_inode_now(dirp);
+ }
+compose_and_out:
+ fh_compose(resfhp, fhp->fh_export, dnew);
+out:
+ return (err ? nfserrno(-err) : 0);
}
/*
@@ -709,37 +702,69 @@ int
nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
char *fname, int len, struct svc_fh *tfhp)
{
+ struct dentry *ddir, *dnew;
struct inode *dirp, *dest;
int err;
- if ((err = fh_lookup(rqstp, ffhp, S_IFDIR, MAY_CREATE) != 0) ||
- (err = fh_lookup(rqstp, tfhp, S_IFREG, MAY_NOP)) != 0)
+ if ((err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_CREATE) != 0) ||
+ (err = fh_verify(rqstp, tfhp, S_IFREG, MAY_NOP)) != 0)
return err;
- dirp = ffhp->fh_inode;
- dest = tfhp->fh_inode;
+ ddir = ffhp->fh_handle.fh_dentry;
+ dirp = ddir->d_inode;
+
+ dnew = lookup_dentry(fname, dget(ddir), 1);
+ err = PTR_ERR(dnew);
+ if (IS_ERR(dnew))
+ return nfserrno(-err);
+
+ err = -EEXIST;
+ if (dnew->d_inode)
+ goto dput_and_out;
+ dest = tfhp->fh_handle.fh_dentry->d_inode;
+
+ err = -EPERM;
if (!len)
- return nfserr_perm;
- if (nfsd_iscovered(dirp))
- return nfserr_acces;
+ goto dput_and_out;
+
+ err = -EACCES;
+ if (nfsd_iscovered(ddir, ffhp->fh_export))
+ goto dput_and_out;
if (dirp->i_dev != dest->i_dev)
- return nfserr_acces; /* FIXME: nxdev for NFSv3 */
+ goto dput_and_out; /* FIXME: nxdev for NFSv3 */
+
+ err = -EPERM;
if (IS_IMMUTABLE(dest) /* || IS_APPEND(dest) */ )
- return nfserr_perm;
+ goto dput_and_out;
if (!dirp->i_op || !dirp->i_op->link)
- return nfserr_perm;
+ goto dput_and_out;
- fh_lock(ffhp); /* lock directory inode */
- dirp->i_count++;
- err = dirp->i_op->link(dest, dirp, fname, len);
- fh_unlock(ffhp); /* unlock inode */
+ fh_lock(ffhp);
+ err = dirp->i_op->link(dest, dirp, dnew);
+ fh_unlock(ffhp);
if (!err && EX_ISSYNC(ffhp->fh_export)) {
- nfsd_write_inode(dirp);
- nfsd_write_inode(dest);
+ write_inode_now(dirp);
+ write_inode_now(dest);
}
+dput_and_out:
+ dput(dnew);
+ return (err ? nfserrno(-err) : 0);
+}
- return err? nfserrno(-err) : 0;
+/* More "hidden treasure" from the generic VFS. -DaveM */
+static inline void nfsd_double_down(struct semaphore *s1, struct semaphore *s2)
+{
+ if((unsigned long) s1 < (unsigned long) s2) {
+ down(s1);
+ down(s2);
+ } else if(s1 == s2) {
+ down(s1);
+ atomic_dec(&s1->count);
+ } else {
+ down(s2);
+ down(s1);
+ }
}
/*
@@ -749,14 +774,19 @@ int
nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
struct svc_fh *tfhp, char *tname, int tlen)
{
+ struct dentry *fdentry, *tdentry, *odentry, *ndentry;
struct inode *fdir, *tdir;
int err;
- if ((err = fh_lookup(rqstp, ffhp, S_IFDIR, MAY_REMOVE) != 0)
- || (err = fh_lookup(rqstp, tfhp, S_IFDIR, MAY_CREATE)) != 0)
+ if ((err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_REMOVE) != 0)
+ || (err = fh_verify(rqstp, tfhp, S_IFDIR, MAY_CREATE)) != 0)
return err;
- fdir = ffhp->fh_inode;
- tdir = tfhp->fh_inode;
+
+ fdentry = ffhp->fh_handle.fh_dentry;
+ fdir = fdentry->d_inode;
+
+ tdentry = tfhp->fh_handle.fh_dentry;
+ tdir = tdentry->d_inode;
if (!flen || (fname[0] == '.' &&
(flen == 1 || (flen == 2 && fname[1] == '.'))) ||
@@ -764,23 +794,37 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
(tlen == 1 || (tlen == 2 && tname[1] == '.'))))
return nfserr_perm;
- if (fdir->i_dev != tdir->i_dev)
- return nfserr_acces; /* nfserr_nxdev */
- if (!fdir->i_op || !fdir->i_op->rename)
- return nfserr_perm;
+ odentry = lookup_dentry(fname, dget(fdentry), 1);
+ err = PTR_ERR(odentry);
+ if (IS_ERR(odentry))
+ goto out_no_unlock;
- fh_lock(tfhp); /* lock destination directory */
- tdir->i_count++;
- fdir->i_count++;
- err = fdir->i_op->rename(fdir, fname, flen, tdir, tname, tlen);
- fh_unlock(tfhp); /* unlock inode */
+ ndentry = lookup_dentry(tname, dget(tdentry), 1);
+ err = PTR_ERR(ndentry);
+ if (IS_ERR(ndentry))
+ goto out_dput_old;
+ nfsd_double_down(&tdir->i_sem, &fdir->i_sem);
+ err = -EXDEV;
+ if (fdir->i_dev != tdir->i_dev)
+ goto out_unlock;
+ err = -EPERM;
+ if (!fdir->i_op || !fdir->i_op->rename)
+ goto out_unlock;
+ err = fdir->i_op->rename(fdir, odentry, tdir, ndentry);
if (!err && EX_ISSYNC(tfhp->fh_export)) {
- nfsd_write_inode(fdir);
- nfsd_write_inode(tdir);
+ write_inode_now(fdir);
+ write_inode_now(tdir);
}
-
- return err? nfserrno(-err) : 0;
+out_unlock:
+ up(&tdir->i_sem);
+ up(&fdir->i_sem);
+
+ dput(ndentry);
+out_dput_old:
+ dput(odentry);
+out_no_unlock:
+ return (err ? nfserrno(-err) : 0);
}
/*
@@ -790,35 +834,40 @@ int
nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
char *fname, int flen)
{
+ struct dentry *dentry, *rdentry;
struct inode *dirp;
int err;
if (!flen || isdotent(fname, flen))
return nfserr_acces;
-
- if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_REMOVE)) != 0)
+ if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_REMOVE)) != 0)
return err;
- fh_lock(fhp); /* lock inode */
- dirp = fhp->fh_inode;
+ dentry = fhp->fh_handle.fh_dentry;
+ dirp = dentry->d_inode;
+
+ rdentry = lookup_dentry(fname, dget(dentry), 0);
+ err = PTR_ERR(rdentry);
+ if (IS_ERR(rdentry))
+ goto out;
+ fh_lock(fhp);
if (type == S_IFDIR) {
- if (!dirp->i_op || !dirp->i_op->rmdir)
- return nfserr_notdir;
- dirp->i_count++;
- err = dirp->i_op->rmdir(dirp, fname, flen);
- } else { /* other than S_IFDIR */
- if (!dirp->i_op || !dirp->i_op->unlink)
- return nfserr_perm;
- dirp->i_count++;
- err = dirp->i_op->unlink(dirp, fname, flen);
+ err = -ENOTDIR;
+ if (dirp->i_op && dirp->i_op->rmdir)
+ err = dirp->i_op->rmdir(dirp, rdentry);
+ } else {
+ err = -EPERM;
+ if (dirp->i_op && dirp->i_op->unlink)
+ err = dirp->i_op->unlink(dirp, rdentry);
}
+ fh_unlock(fhp);
- fh_unlock(fhp); /* unlock inode */
+ dput(rdentry);
if (!err && EX_ISSYNC(fhp->fh_export))
- nfsd_write_inode(dirp);
-
- return err? nfserrno(-err) : 0;
+ write_inode_now(dirp);
+out:
+ return (err ? nfserrno(-err) : 0);
}
/*
@@ -829,6 +878,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
encode_dent_fn func, u32 *buffer, int *countp)
{
struct readdir_cd cd;
+ struct inode *inode;
struct file file;
u32 *p;
int oldlen, eof, err;
@@ -856,6 +906,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
* readdir() is not guaranteed to fill up the entire buffer, but
* may choose to do less.
*/
+ inode = file.f_dentry->d_inode;
do {
oldlen = cd.buflen;
@@ -864,7 +915,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
file.f_inode->i_dev, file.f_inode->i_ino,
(int) file.f_pos, (int) oldlen, (int) cd.buflen);
*/
- err = file.f_op->readdir(file.f_inode, &file,
+ err = file.f_op->readdir(inode, &file,
&cd, (filldir_t) func);
if (err < 0) {
@@ -904,14 +955,16 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
int
nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct statfs *stat)
{
+ struct dentry *dentry;
struct inode *inode;
struct super_block *sb;
unsigned long oldfs;
int err;
- if ((err = fh_lookup(rqstp, fhp, 0, MAY_NOP)) != 0)
+ if ((err = fh_verify(rqstp, fhp, 0, MAY_NOP)) != 0)
return err;
- inode = fhp->fh_inode;
+ dentry = fhp->fh_handle.fh_dentry;
+ inode = dentry->d_inode;
if (!(sb = inode->i_sb) || !sb->s_op->statfs)
return nfserr_io;
@@ -928,8 +981,9 @@ nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct statfs *stat)
* Check for a user's access permissions to this inode.
*/
int
-nfsd_permission(struct svc_export *exp, struct inode *inode, int acc)
+nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc)
{
+ struct inode *inode = dentry->d_inode;
int err;
if (acc == MAY_NOP)
@@ -954,7 +1008,7 @@ nfsd_permission(struct svc_export *exp, struct inode *inode, int acc)
if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) {
if (EX_RDONLY(exp) || IS_RDONLY(inode))
return nfserr_rofs;
- if (S_ISDIR(inode->i_mode) && nfsd_iscovered(inode))
+ if (S_ISDIR(inode->i_mode) && nfsd_iscovered(dentry, exp))
return nfserr_perm;
if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode))
return nfserr_perm;
@@ -984,80 +1038,6 @@ nfsd_permission(struct svc_export *exp, struct inode *inode, int acc)
}
/*
- * Look up the inode for a given FH.
- */
-struct inode *
-nfsd_iget(dev_t dev, ino_t ino)
-{
- struct super_block *sb;
-
- if (!(sb = get_super(dev)) || fs_off_limits(sb))
- return NULL;
- return __iget(sb, ino, 0);
-}
-
-/*
- * Write the inode if dirty (copy of fs/inode.c:write_inode)
- */
-void
-nfsd_write_inode(struct inode *inode)
-{
- struct super_block *sb = inode->i_sb;
-
- if (!inode->i_dirt)
- return;
- while (inode->i_lock) {
- sleep_on(&inode->i_wait);
- if (!inode->i_dirt)
- return;
- }
- if (!sb || !sb->s_op || !sb->s_op->write_inode) {
- inode->i_dirt = 0;
- return;
- }
- inode->i_lock = 1;
- sb->s_op->write_inode(inode);
- inode->i_lock = 0;
- wake_up(&inode->i_wait);
-}
-
-/*
- * Look up the root inode of the parent fs.
- * We have to go through iget in order to allow for wait_on_inode.
- */
-int
-nfsd_parentdev(dev_t* devp)
-{
- struct super_block *sb;
-
- if (!(sb = get_super(*devp)) || !sb->s_covered)
- return 0;
- if (*devp == sb->s_covered->i_dev)
- return 0;
- *devp = sb->s_covered->i_dev;
- return 1;
-}
-
-/*
- * This is a copy from fs/inode.c because it wasn't exported.
- */
-int
-nfsd_notify_change(struct inode *inode, struct iattr *attr)
-{
- int retval;
-
- if (inode->i_sb && inode->i_sb->s_op &&
- inode->i_sb->s_op->notify_change)
- return inode->i_sb->s_op->notify_change(inode, attr);
-
- if ((retval = inode_change_ok(inode, attr)) != 0)
- return retval;
-
- inode_setattr(inode, attr);
- return 0;
-}
-
-/*
* Initialize readahead param cache
*/
void
diff --git a/fs/open.c b/fs/open.c
index 3408fb2a6..36669e4a6 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -543,44 +543,21 @@ out:
return error;
}
-asmlinkage int sys_lchown(const char * filename, uid_t user, gid_t group)
-{
- struct dentry * dentry;
- int error;
-
- lock_kernel();
- dentry = lnamei(filename);
-
- error = PTR_ERR(dentry);
- if (IS_ERR(dentry))
- goto out;
-
- error = chown_common(dentry, user, group);
-
- dput(dentry);
-out:
- unlock_kernel();
- return(error);
-}
-
asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group)
{
struct dentry * dentry;
int error;
lock_kernel();
- dentry = namei(filename);
+ dentry = lnamei(filename);
error = PTR_ERR(dentry);
- if (IS_ERR(dentry))
- goto out;
-
- error = chown_common(dentry, user, group);
-
- dput(dentry);
-out:
+ if (!IS_ERR(dentry)) {
+ error = chown_common(dentry, user, group);
+ dput(dentry);
+ }
unlock_kernel();
- return(error);
+ return error;
}
asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group)
diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c
index 5d936ee59..d9a334f85 100644
--- a/fs/romfs/inode.c
+++ b/fs/romfs/inode.c
@@ -18,12 +18,19 @@
* Changes
* Changed for 2.1.19 modules
* Jan 1997 Initial release
+ * Jun 1997 2.1.43+ changes
+ * Jul 1997 proper page locking in readpage
+ * Changed to work with 2.1.45+ fs
+ * Fixed follow_link
*/
/* todo:
- * use malloced memory for file names?
- * considering write access...
- * network (tftp) files?
+ * - see Documentation/filesystems/romfs.txt
+ * - use malloced memory for file names?
+ * - considering write access...
+ * - network (tftp) files?
+ * - in the ancient times something leaked to made umounts
+ * impossible, but I've not seen it in the last months
*/
/*
@@ -74,7 +81,7 @@ romfs_read_super(struct super_block *s, void *data, int silent)
MOD_INC_USE_COUNT;
- /* I would parse the options, but there are none.. :) */
+ /* I would parse the options here, but there are none.. :) */
lock_super(s);
set_blocksize(dev, ROMBSIZE);
@@ -82,6 +89,7 @@ romfs_read_super(struct super_block *s, void *data, int silent)
s->s_blocksize_bits = ROMBSBITS;
bh = bread(dev, 0, ROMBSIZE);
if (!bh) {
+ /* XXX merge with other printk? */
printk ("romfs: unable to read superblock\n");
goto outnobh;
}
@@ -113,10 +121,11 @@ romfs_read_super(struct super_block *s, void *data, int silent)
brelse(bh);
s->s_op = &romfs_ops;
+ s->s_root = d_alloc_root(iget(s, sz), NULL);
unlock_super(s);
- if (!(s->s_mounted = iget(s, sz)))
+ if (!s->s_root)
goto outnobh;
/* Ehrhm; sorry.. :) And thanks to Hans-Joachim Widmaier :) */
@@ -145,10 +154,9 @@ romfs_put_super(struct super_block *sb)
return;
}
-
/* That's simple too. */
-static void
+static int
romfs_statfs(struct super_block *sb, struct statfs *buf, int bufsize)
{
struct statfs tmp;
@@ -157,9 +165,12 @@ romfs_statfs(struct super_block *sb, struct statfs *buf, int bufsize)
tmp.f_type = ROMFS_MAGIC;
tmp.f_bsize = ROMBSIZE;
tmp.f_blocks = (sb->u.romfs_sb.s_maxsize+ROMBSIZE-1)>>ROMBSBITS;
- copy_to_user(buf, &tmp, bufsize);
+ /* XXX tmp.f_namelen = relevant? */
+ return copy_to_user(buf, &tmp, bufsize) ? -EFAULT : 0;
}
+/* some helper routines */
+
static int
romfs_strnlen(struct inode *i, unsigned long offset, unsigned long count)
{
@@ -238,8 +249,6 @@ romfs_copyfrom(struct inode *i, void *dest, unsigned long offset, unsigned long
return res;
}
-/* Directory operations */
-
static int
romfs_readdir(struct inode *i, struct file *filp, void *dirent, filldir_t filldir)
{
@@ -295,14 +304,16 @@ romfs_readdir(struct inode *i, struct file *filp, void *dirent, filldir_t filldi
}
static int
-romfs_lookup(struct inode *dir, const char *name, int len, struct inode **result)
+romfs_lookup(struct inode *dir, struct dentry *dentry)
{
unsigned long offset, maxoff;
int fslen, res;
+ struct inode *inode;
char fsname[ROMFS_MAXFN]; /* XXX dynamic? */
struct romfs_inode ri;
+ const char *name; /* got from dentry */
+ int len;
- *result = NULL;
if (!dir || !S_ISDIR(dir->i_mode)) {
res = -EBADF;
goto out;
@@ -317,6 +328,12 @@ romfs_lookup(struct inode *dir, const char *name, int len, struct inode **result
maxoff = dir->i_sb->u.romfs_sb.s_maxsize;
offset = ntohl(ri.spec) & ROMFH_MASK;
+ /* ok, now find the file, whose name is in "dentry", in the
+ * directory specified by "dir". */
+
+ name = dentry->d_name.name;
+ len = dentry->d_name.len;
+
for(;;) {
if (!offset || offset >= maxoff
|| romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) {
@@ -350,20 +367,19 @@ romfs_lookup(struct inode *dir, const char *name, int len, struct inode **result
if ((ntohl(ri.next) & ROMFH_TYPE) == ROMFH_HRD)
offset = ntohl(ri.spec) & ROMFH_MASK;
- res = 0;
- if (!(*result = iget(dir->i_sb, offset)))
- res = -EACCES;
+ res = -EACCES;
+ if ((inode = iget(dir->i_sb, offset))!=NULL) {
+ res = 0;
+ d_add(dentry, inode);
+ }
out:
- iput(dir);
return res;
}
/*
* Ok, we do readpage, to be able to execute programs. Unfortunately,
- * bmap is not applicable, since we have looser alignments.
- *
- * XXX I'm not quite sure that I need to muck around the PG_xx bits..
+ * we can't use bmap, since we have looser alignments.
*/
static int
@@ -373,8 +389,12 @@ romfs_readpage(struct inode * inode, struct page * page)
unsigned long offset, avail, readlen;
int result = -EIO;
- buf = page_address(page);
atomic_inc(&page->count);
+ set_bit(PG_locked, &page->flags);
+
+ buf = page_address(page);
+ clear_bit(PG_uptodate, &page->flags);
+ clear_bit(PG_error, &page->flags);
offset = page->offset;
if (offset < inode->i_size) {
avail = inode->i_size-offset;
@@ -383,13 +403,19 @@ romfs_readpage(struct inode * inode, struct page * page)
if (readlen < PAGE_SIZE) {
memset((void *)(buf+readlen),0,PAGE_SIZE-readlen);
}
- result = 0;
set_bit(PG_uptodate, &page->flags);
- } else {
- memset((void *)buf, 0, PAGE_SIZE);
+ result = 0;
}
}
+ if (result) {
+ set_bit(PG_error, &page->flags);
+ memset((void *)buf, 0, PAGE_SIZE);
+ }
+
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
free_page(buf);
+
return result;
}
@@ -417,6 +443,46 @@ out:
return mylen;
}
+static struct dentry *romfs_follow_link(struct inode *inode, struct dentry *base)
+{
+ char *link;
+ int len, cnt;
+ struct dentry *dentry;
+
+ /* Note: 2.1.46+ calls this for our strange directories...
+ * What I do is not really right, but I like it better for now,
+ * than a separate i_op table. Anyway, our directories won't
+ * have multiple "real" links to them, so it maybe loses nothing. */
+ if (!S_ISLNK(inode->i_mode)) {
+ dentry = dget(i_dentry(inode));
+ goto outnobuf;
+ }
+
+ len = inode->i_size;
+
+ dentry = ERR_PTR(-EAGAIN); /* correct? */
+ if (!(link = kmalloc(len+1, GFP_KERNEL)))
+ goto outnobuf;
+
+ cnt = romfs_copyfrom(inode, link, inode->u.romfs_i.i_dataoffset, len);
+ if (len != cnt) {
+ dentry = ERR_PTR(-EIO);
+ goto out;
+ } else
+ link[len] = 0;
+
+ dentry = lookup_dentry(link, base, 1);
+ kfree(link);
+
+ if (0) {
+out:
+ kfree(link);
+outnobuf:
+ dput(base);
+ }
+ return dentry;
+}
+
/* Mapping from our types to the kernel */
static struct file_operations romfs_file_operations = {
@@ -447,6 +513,7 @@ static struct inode_operations romfs_file_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
romfs_readpage, /* readpage */
NULL, /* writepage */
NULL, /* bmap -- not really */
@@ -471,7 +538,7 @@ static struct file_operations romfs_dir_operations = {
NULL /* revalidate */
};
-/* Merged dir/symlink op table. readdir/lookup/readlink
+/* Merged dir/symlink op table. readdir/lookup/readlink/follow_link
* will protect from type mismatch.
*/
@@ -487,6 +554,7 @@ static struct inode_operations romfs_dirlink_inode_operations = {
NULL, /* mknod */
NULL, /* rename */
romfs_readlink, /* readlink */
+ romfs_follow_link, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
@@ -519,9 +587,9 @@ romfs_read_inode(struct inode *i)
int nextfh, ino;
struct romfs_inode ri;
- i->i_op = NULL;
-
ino = i->i_ino & ROMFH_MASK;
+ i->i_op = NULL;
+ i->i_mode = 0;
/* Loop for finding the real hard link */
for(;;) {
@@ -549,6 +617,7 @@ romfs_read_inode(struct inode *i)
ino = ((ROMFH_SIZE+ino+1+ROMFH_PAD)&ROMFH_MASK);
else
ino = 0;
+
i->u.romfs_i.i_metasize = ino;
i->u.romfs_i.i_dataoffset = ino+(i->i_ino&ROMFH_MASK);
@@ -573,9 +642,10 @@ romfs_read_inode(struct inode *i)
static struct super_operations romfs_ops = {
romfs_read_inode, /* read inode */
- NULL, /* notify change */
NULL, /* write inode */
NULL, /* put inode */
+ NULL, /* delete inode */
+ NULL, /* notify change */
romfs_put_super, /* put super */
NULL, /* write super */
romfs_statfs, /* statfs */
diff --git a/fs/super.c b/fs/super.c
index f143f9348..a7a2d434e 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -637,6 +637,7 @@ static int umount_dev(kdev_t dev)
if (MAJOR(dev) >= MAX_BLKDEV)
return -ENXIO;
+ fsync_dev(dev);
retval = do_umount(dev,0);
if (!retval) {
fsync_dev(dev);