summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-12-01 04:02:08 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-12-01 04:02:08 +0000
commitfd095d09f2d475dc2e8599b1b8bae1cd65e91685 (patch)
tree217f87a997699505e0dd752931409b9f10fffe65 /fs
parentc02e0599c4233f97071928f8118841954bacdadf (diff)
Merge with 2.1.56 as first part of merging back my code.
Diffstat (limited to 'fs')
-rw-r--r--fs/Makefile2
-rw-r--r--fs/affs/dir.c2
-rw-r--r--fs/bad_inode.c83
-rw-r--r--fs/binfmt_aout.c4
-rw-r--r--fs/binfmt_elf.c4
-rw-r--r--fs/block_dev.c9
-rw-r--r--fs/buffer.c17
-rw-r--r--fs/dcache.c20
-rw-r--r--fs/exec.c2
-rw-r--r--fs/ext2/file.c5
-rw-r--r--fs/ext2/fsync.c44
-rw-r--r--fs/fat/mmap.c4
-rw-r--r--fs/fcntl.c86
-rw-r--r--fs/inode.c171
-rw-r--r--fs/lockd/clntlock.c2
-rw-r--r--fs/locks.c230
-rw-r--r--fs/minix/fsync.c8
-rw-r--r--fs/namei.c8
-rw-r--r--fs/ncpfs/file.c2
-rw-r--r--fs/ncpfs/mmap.c4
-rw-r--r--fs/nfs/file.c17
-rw-r--r--fs/nfs/write.c34
-rw-r--r--fs/nfsd/vfs.c5
-rw-r--r--fs/open.c2
-rw-r--r--fs/pipe.c3
-rw-r--r--fs/proc/generic.c6
-rw-r--r--fs/proc/mem.c9
-rw-r--r--fs/proc/scsi.c5
-rw-r--r--fs/read_write.c19
-rw-r--r--fs/smbfs/dir.c541
-rw-r--r--fs/smbfs/file.c120
-rw-r--r--fs/smbfs/inode.c232
-rw-r--r--fs/smbfs/proc.c442
-rw-r--r--fs/smbfs/sock.c371
-rw-r--r--fs/sysv/fsync.c3
35 files changed, 1583 insertions, 933 deletions
diff --git a/fs/Makefile b/fs/Makefile
index 6061adad6..871524350 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -13,7 +13,7 @@ O_TARGET := fs.o
O_OBJS = open.o read_write.o devices.o file_table.o buffer.o \
super.o block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
ioctl.o readdir.o select.o fifo.o locks.o filesystems.o \
- inode.o dcache.o attr.o $(BINFMTS)
+ inode.o dcache.o attr.o bad_inode.o $(BINFMTS)
MOD_LIST_NAME := FS_MODULES
ALL_SUB_DIRS = minix ext2 fat msdos vfat proc isofs nfs umsdos \
diff --git a/fs/affs/dir.c b/fs/affs/dir.c
index e936e5bb5..cfd05640b 100644
--- a/fs/affs/dir.c
+++ b/fs/affs/dir.c
@@ -82,7 +82,7 @@ affs_readdir(struct file *filp, void *dirent, filldir_t filldir)
struct buffer_head *dir_bh;
struct buffer_head *fh_bh;
struct inode *dir;
- struct inode *inode = file->f_dentry->d_inode;
+ struct inode *inode = filp->f_dentry->d_inode;
pr_debug("AFFS: readdir(ino=%ld,f_pos=%lu)\n",inode->i_ino,filp->f_pos);
diff --git a/fs/bad_inode.c b/fs/bad_inode.c
new file mode 100644
index 000000000..562a5d8b7
--- /dev/null
+++ b/fs/bad_inode.c
@@ -0,0 +1,83 @@
+/*
+ * linux/fs/bad_inode.c
+ *
+ * Copyright (C) 1997, Stephen Tweedie
+ *
+ * Provide stub functions for unreadable inodes
+ */
+
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+
+/*
+ * The follow_symlink operation must dput() the base.
+ */
+static struct dentry * bad_follow_link(struct inode * ino, struct dentry *base)
+{
+ dput(base);
+ return ERR_PTR(-EIO);
+}
+
+static int return_EIO()
+{
+ return -EIO;
+}
+
+#define EIO_ERROR ((void *) (return_EIO))
+
+static struct file_operations bad_file_ops =
+{
+ EIO_ERROR, /* lseek */
+ EIO_ERROR, /* read */
+ EIO_ERROR, /* write */
+ EIO_ERROR, /* readdir */
+ EIO_ERROR, /* select */
+ EIO_ERROR, /* ioctl */
+ EIO_ERROR, /* mmap */
+ EIO_ERROR, /* open */
+ EIO_ERROR, /* release */
+ EIO_ERROR, /* fsync */
+ EIO_ERROR, /* fasync */
+ EIO_ERROR, /* check_media_change */
+ EIO_ERROR /* revalidate */
+};
+
+struct inode_operations bad_inode_ops =
+{
+ &bad_file_ops, /* default file operations */
+ EIO_ERROR, /* create */
+ EIO_ERROR, /* lookup */
+ EIO_ERROR, /* link */
+ EIO_ERROR, /* unlink */
+ EIO_ERROR, /* symlink */
+ EIO_ERROR, /* mkdir */
+ EIO_ERROR, /* rmdir */
+ EIO_ERROR, /* mknod */
+ EIO_ERROR, /* rename */
+ EIO_ERROR, /* readlink */
+ bad_follow_link, /* follow_link */
+ EIO_ERROR, /* readpage */
+ EIO_ERROR, /* writepage */
+ EIO_ERROR, /* bmap */
+ EIO_ERROR, /* truncate */
+ EIO_ERROR, /* permission */
+ EIO_ERROR /* smap */
+};
+
+
+/*
+ * When a filesystem is unable to read an inode due to an I/O error in
+ * its read_inode() function, it can call make_bad_inode() to return a
+ * set of stubs which will return EIO errors as required.
+ *
+ * We only need to do limited initialisation: all other fields are
+ * preinitialised to zero automatically.
+ */
+void make_bad_inode(struct inode * inode)
+{
+ inode->i_mode = S_IFREG;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_op = &bad_inode_ops;
+}
+
diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c
index 0ff5b7ff6..2e9733706 100644
--- a/fs/binfmt_aout.c
+++ b/fs/binfmt_aout.c
@@ -62,7 +62,7 @@ while (file.f_op->write(inode,&file,(char *)(addr),(nr)) != (nr)) goto close_cor
#define DUMP_SEEK(offset) \
if (file.f_op->llseek) { \
- if (file.f_op->llseek(inode,&file,(offset),0) != (offset)) \
+ if (file.f_op->llseek(&file,(offset),0) != (offset)) \
goto close_coredump; \
} else file.f_pos = (offset)
@@ -492,7 +492,7 @@ do_load_aout_library(int fd)
/* Seek into the file */
if (file->f_op->llseek) {
- if ((error = file->f_op->llseek(inode, file, 0, 0)) != 0)
+ if ((error = file->f_op->llseek(file, 0, 0)) != 0)
return -ENOEXEC;
} else
file->f_pos = 0;
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 837ce563b..5187ae1cb 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -852,7 +852,7 @@ do_load_elf_library(int fd){
/* seek to the beginning of the file */
if (file->f_op->llseek) {
- if ((error = file->f_op->llseek(inode, file, 0, 0)) != 0)
+ if ((error = file->f_op->llseek(file, 0, 0)) != 0)
return -ENOEXEC;
} else
file->f_pos = 0;
@@ -961,7 +961,7 @@ static int dump_write(struct file *file, const void *addr, int nr)
static int dump_seek(struct file *file, off_t off)
{
if (file->f_op->llseek) {
- if (file->f_op->llseek(file->f_dentry->d_inode, file, off, 0) != off)
+ if (file->f_op->llseek(file, off, 0) != off)
return 0;
} else
file->f_pos = off;
diff --git a/fs/block_dev.c b/fs/block_dev.c
index f42026ac2..dbb9d14dc 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -287,7 +287,12 @@ long block_read(struct inode * inode, struct file * filp,
return read;
}
-int block_fsync(struct inode *inode, struct file *filp)
+/*
+ * Filp may be NULL when we are called by an msync of a vma
+ * since the vma has no handle.
+ */
+
+int block_fsync(struct file *filp, struct dentry *dentry)
{
- return fsync_dev (inode->i_rdev);
+ return fsync_dev(dentry->d_inode->i_rdev);
}
diff --git a/fs/buffer.c b/fs/buffer.c
index 62c4aa318..6025dcd31 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -107,7 +107,7 @@ union bdflush_param{
int dummy3; /* unused */
} b_un;
unsigned int data[N_PARAM];
-} bdf_prm = {{60, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}};
+} bdf_prm = {{40, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}};
/* These are the min and max parameter values that we will allow to be assigned */
int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 0, 100, 100, 1, 1};
@@ -282,9 +282,13 @@ asmlinkage int sys_sync(void)
return 0;
}
-int file_fsync (struct inode *inode, struct file *filp)
+/*
+ * filp may be NULL if called via the msync of a vma.
+ */
+
+int file_fsync(struct file *filp, struct dentry *dentry)
{
- return fsync_dev(inode->i_dev);
+ return fsync_dev(dentry->d_inode->i_dev);
}
asmlinkage int sys_fsync(unsigned int fd)
@@ -316,7 +320,10 @@ asmlinkage int sys_fsync(unsigned int fd)
if (!file->f_op || !file->f_op->fsync)
goto out;
- err = file->f_op->fsync(inode,file);
+ /* We need to protect against concurrent writers.. */
+ down(&inode->i_sem);
+ err = file->f_op->fsync(file, file->f_dentry);
+ up(&inode->i_sem);
out:
unlock_kernel();
@@ -353,7 +360,7 @@ asmlinkage int sys_fdatasync(unsigned int fd)
goto out;
/* this needs further work, at the moment it is identical to fsync() */
- err = file->f_op->fsync(inode,file);
+ err = file->f_op->fsync(file, file->f_dentry);
out:
unlock_kernel();
diff --git a/fs/dcache.c b/fs/dcache.c
index afc24db1e..600e5b0e3 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -119,7 +119,7 @@ int d_invalidate(struct dentry * dentry)
* something (at which point we need to unuse
* all dentries).
*/
-void shrink_dcache(void)
+void prune_dcache(int count)
{
for (;;) {
struct dentry *dentry;
@@ -143,6 +143,8 @@ void shrink_dcache(void)
parent = dentry->d_parent;
d_free(dentry);
dput(parent);
+ if (!--count)
+ break;
}
}
}
@@ -177,7 +179,12 @@ struct dentry * d_alloc(struct dentry * parent, const struct qstr *name)
dentry->d_count = 1;
dentry->d_flags = 0;
dentry->d_inode = NULL;
- dentry->d_parent = dget(parent);
+ dentry->d_parent = NULL;
+ dentry->d_sb = NULL;
+ if (parent) {
+ dentry->d_parent = dget(parent);
+ dentry->d_sb = parent->d_sb;
+ }
dentry->d_mounts = dentry;
dentry->d_covers = dentry;
INIT_LIST_HEAD(&dentry->d_hash);
@@ -211,8 +218,11 @@ struct dentry * d_alloc_root(struct inode * root_inode, struct dentry *old_root)
if (root_inode) {
res = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 });
- res->d_parent = res;
- d_instantiate(res, root_inode);
+ if (res) {
+ res->d_sb = root_inode->i_sb;
+ res->d_parent = res;
+ d_instantiate(res, root_inode);
+ }
}
return res;
}
@@ -344,7 +354,7 @@ void d_add(struct dentry * entry, struct inode * inode)
x = y; y = __tmp; } while (0)
/*
- * We cannibalize "newdentry" when moving dentry on top of it,
+ * We cannibalize "target" when moving dentry on top of it,
* because it's going to be thrown away anyway. We could be more
* polite about it, though.
*
diff --git a/fs/exec.c b/fs/exec.c
index c7d80e877..027d8ceb6 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -369,7 +369,7 @@ int read_exec(struct dentry *dentry, unsigned long offset,
if (!file.f_op->read)
goto close_readexec;
if (file.f_op->llseek) {
- if (file.f_op->llseek(inode,&file,offset,0) != offset)
+ if (file.f_op->llseek(&file,offset,0) != offset)
goto close_readexec;
} else
file.f_pos = offset;
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index d632133f1..b5f55efd6 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -36,7 +36,7 @@
#include <linux/fs.h>
#include <linux/ext2_fs.h>
-static long long ext2_file_lseek(struct inode *, struct file *, long long, int);
+static long long ext2_file_lseek(struct file *, long long, int);
static long ext2_file_write (struct inode *, struct file *, const char *, unsigned long);
static int ext2_release_file (struct inode *, struct file *);
@@ -84,12 +84,13 @@ struct inode_operations ext2_file_inode_operations = {
/*
* Make sure the offset never goes beyond the 32-bit mark..
*/
-static long long ext2_file_lseek(struct inode *inode,
+static long long ext2_file_lseek(
struct file *file,
long long offset,
int origin)
{
long long retval;
+ struct inode *inode = file->f_dentry->d_inode;
switch (origin) {
case 2:
diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c
index 9993af1a6..8a9bdf902 100644
--- a/fs/ext2/fsync.c
+++ b/fs/ext2/fsync.c
@@ -38,18 +38,12 @@
static int sync_block (struct inode * inode, u32 * block, int wait)
{
struct buffer_head * bh;
- int tmp;
if (!*block)
return 0;
- tmp = *block;
bh = get_hash_table (inode->i_dev, *block, blocksize);
if (!bh)
return 0;
- if (*block != tmp) {
- brelse (bh);
- return 1;
- }
if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
brelse (bh);
return -1;
@@ -67,18 +61,12 @@ static int sync_block (struct inode * inode, u32 * block, int wait)
static int sync_block_swab32 (struct inode * inode, u32 * block, int wait)
{
struct buffer_head * bh;
- int tmp;
if (!le32_to_cpu(*block))
return 0;
- tmp = le32_to_cpu(*block);
bh = get_hash_table (inode->i_dev, le32_to_cpu(*block), blocksize);
if (!bh)
return 0;
- if (le32_to_cpu(*block) != tmp) {
- brelse (bh);
- return 1;
- }
if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
brelse (bh);
return -1;
@@ -109,11 +97,6 @@ static int sync_iblock (struct inode * inode, u32 * iblock,
if (rc)
return rc;
*bh = bread (inode->i_dev, tmp, blocksize);
- if (tmp != *iblock) {
- brelse (*bh);
- *bh = NULL;
- return 1;
- }
if (!*bh)
return -1;
return 0;
@@ -133,11 +116,6 @@ static int sync_iblock_swab32 (struct inode * inode, u32 * iblock,
if (rc)
return rc;
*bh = bread (inode->i_dev, tmp, blocksize);
- if (tmp != le32_to_cpu(*iblock)) {
- brelse (*bh);
- *bh = NULL;
- return 1;
- }
if (!*bh)
return -1;
return 0;
@@ -153,8 +131,6 @@ static int sync_direct (struct inode * inode, int wait)
for (i = 0; i < EXT2_NDIR_BLOCKS; i++) {
rc = sync_block (inode, inode->u.ext2_i.i_data + i, wait);
- if (rc > 0)
- break;
if (rc)
err = rc;
}
@@ -175,8 +151,6 @@ static int sync_indirect (struct inode * inode, u32 * iblock, int wait)
rc = sync_block_swab32 (inode,
((u32 *) ind_bh->b_data) + i,
wait);
- if (rc > 0)
- break;
if (rc)
err = rc;
}
@@ -199,8 +173,6 @@ static __inline__ int sync_indirect_swab32 (struct inode * inode, u32 * iblock,
rc = sync_block_swab32 (inode,
((u32 *) ind_bh->b_data) + i,
wait);
- if (rc > 0)
- break;
if (rc)
err = rc;
}
@@ -225,8 +197,6 @@ static int sync_dindirect (struct inode * inode, u32 * diblock, int wait)
rc = sync_indirect_swab32 (inode,
((u32 *) dind_bh->b_data) + i,
wait);
- if (rc > 0)
- break;
if (rc)
err = rc;
}
@@ -249,8 +219,6 @@ static __inline__ int sync_dindirect_swab32 (struct inode * inode, u32 * diblock
rc = sync_indirect_swab32 (inode,
((u32 *) dind_bh->b_data) + i,
wait);
- if (rc > 0)
- break;
if (rc)
err = rc;
}
@@ -275,8 +243,6 @@ static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait)
rc = sync_dindirect_swab32 (inode,
((u32 *) tind_bh->b_data) + i,
wait);
- if (rc > 0)
- break;
if (rc)
err = rc;
}
@@ -284,9 +250,15 @@ static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait)
return err;
}
-int ext2_sync_file (struct inode * inode, struct file * file)
+/*
+ * File may be NULL when we are called. Perhaps we shouldn't
+ * even pass file to fsync ?
+ */
+
+int ext2_sync_file(struct file * file, struct dentry *dentry)
{
int wait, err = 0;
+ struct inode *inode = dentry->d_inode;
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
S_ISLNK(inode->i_mode)))
@@ -312,5 +284,5 @@ int ext2_sync_file (struct inode * inode, struct file * file)
}
skip:
err |= ext2_sync_inode (inode);
- return (err < 0) ? -EIO : 0;
+ return err ? -EIO : 0;
}
diff --git a/fs/fat/mmap.c b/fs/fat/mmap.c
index a858cd2cc..fd66e51d1 100644
--- a/fs/fat/mmap.c
+++ b/fs/fat/mmap.c
@@ -91,8 +91,10 @@ struct vm_operations_struct fat_file_mmap = {
* This is used for a general mmap of an msdos file
* Returns 0 if ok, or a negative error code if not.
*/
-int fat_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
+int fat_mmap(struct file * file, struct vm_area_struct * vma)
{
+ struct inode *inode = file->f_dentry->d_inode;
+
if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */
return -EINVAL;
if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 57eef2530..ce00e439f 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -83,7 +83,7 @@ static int setfl(struct file * filp, unsigned long arg)
/* Did FASYNC state change? */
if ((arg ^ filp->f_flags) & FASYNC) {
if (filp->f_op->fasync)
- filp->f_op->fasync(inode, filp, (arg & FASYNC) != 0);
+ filp->f_op->fasync(filp, (arg & FASYNC) != 0);
}
/* required for strict SunOS emulation */
@@ -98,8 +98,6 @@ static int setfl(struct file * filp, unsigned long arg)
asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
{
struct file * filp;
- struct task_struct *p;
- int task_found = 0;
long err = -EBADF;
lock_kernel();
@@ -142,57 +140,13 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
* current syscall conventions, the only way
* to fix this will be in libc.
*/
- err = filp->f_owner;
+ err = filp->f_owner.pid;
break;
case F_SETOWN:
- /*
- * Add the security checks - AC. Without
- * this there is a massive Linux security
- * hole here - consider what happens if
- * you do something like
- *
- * fcntl(0,F_SETOWN,some_root_process);
- * getchar();
- *
- * and input a line!
- *
- * BTW: Don't try this for fun. Several Unix
- * systems I tried this on fall for the
- * trick!
- *
- * I had to fix this botch job as Linux
- * kill_fasync asserts priv making it a
- * free all user process killer!
- *
- * Changed to make the security checks more
- * liberal. -- TYT
- */
- if (current->pgrp == -arg || current->pid == arg)
- goto fasync_ok;
-
- read_lock(&tasklist_lock);
- for_each_task(p) {
- if ((p->pid == arg) || (p->pid == -arg) ||
- (p->pgrp == -arg)) {
- task_found++;
- err = -EPERM;
- if ((p->session != current->session) &&
- (p->uid != current->uid) &&
- (p->euid != current->euid) &&
- !suser()) {
- read_unlock(&tasklist_lock);
- goto out;
- }
- break;
- }
- }
- read_unlock(&tasklist_lock);
- err = -EINVAL;
- if ((task_found == 0) && !suser())
- break;
- fasync_ok:
err = 0;
- filp->f_owner = arg;
+ filp->f_owner.pid = arg;
+ filp->f_owner.uid = current->uid;
+ filp->f_owner.euid = current->euid;
if (S_ISSOCK (filp->f_dentry->d_inode->i_mode))
err = sock_fcntl (filp, F_SETOWN, arg);
break;
@@ -209,18 +163,40 @@ out:
return err;
}
+static void send_sigio(int pid, uid_t uid, uid_t euid)
+{
+ struct task_struct * p;
+
+ read_lock(&tasklist_lock);
+ for_each_task(p) {
+ int match = p->pid;
+ if (pid < 0)
+ match = -p->pgrp;
+ if (pid != match)
+ continue;
+ if (!euid &&
+ (euid ^ p->suid) && (euid ^ p->uid) &&
+ (uid ^ p->suid) && (uid ^ p->uid))
+ continue;
+ p->signal |= 1 << (SIGIO-1);
+ if (p->state == TASK_INTERRUPTIBLE && (p->signal & ~p->blocked))
+ wake_up_process(p);
+ }
+ read_unlock(&tasklist_lock);
+}
+
void kill_fasync(struct fasync_struct *fa, int sig)
{
while (fa) {
+ struct fown_struct * fown;
if (fa->magic != FASYNC_MAGIC) {
printk("kill_fasync: bad magic number in "
"fasync_struct!\n");
return;
}
- if (fa->fa_file->f_owner > 0)
- kill_proc(fa->fa_file->f_owner, sig, 1);
- else
- kill_pg(-fa->fa_file->f_owner, sig, 1);
+ fown = &fa->fa_file->f_owner;
+ if (fown->pid)
+ send_sigio(fown->pid, fown->uid, fown->euid);
fa = fa->fa_next;
}
}
diff --git a/fs/inode.c b/fs/inode.c
index 7c9881222..6a8423c39 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -125,33 +125,6 @@ static inline void init_once(struct inode * inode)
sema_init(&inode->i_sem, 1);
}
-
-/*
- * Look out! This returns with the inode lock held if
- * it got an inode..
- */
-static struct inode * grow_inodes(void)
-{
- struct inode * inode = (struct inode *)__get_free_page(GFP_KERNEL);
-
- if (inode) {
- int size;
- struct inode * tmp;
-
- spin_lock(&inode_lock);
- size = PAGE_SIZE - 2*sizeof(struct inode);
- tmp = inode;
- do {
- tmp++;
- init_once(tmp);
- list_add(&tmp->i_list, &inode_unused);
- size -= sizeof(struct inode);
- } while (size >= 0);
- init_once(inode);
- }
- return inode;
-}
-
static inline void write_inode(struct inode *inode)
{
if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->write_inode)
@@ -238,7 +211,8 @@ void write_inode_now(struct inode *inode)
*/
void clear_inode(struct inode *inode)
{
- truncate_inode_pages(inode, 0);
+ if (inode->i_nrpages)
+ truncate_inode_pages(inode, 0);
wait_on_inode(inode);
if (IS_WRITABLE(inode) && inode->i_sb && inode->i_sb->dq_op)
inode->i_sb->dq_op->drop(inode);
@@ -253,6 +227,7 @@ void clear_inode(struct inode *inode)
static void dispose_list(struct list_head * head)
{
struct list_head *next;
+ int count = 0;
next = head->next;
for (;;) {
@@ -263,12 +238,14 @@ static void dispose_list(struct list_head * head)
if (tmp == head)
break;
inode = list_entry(tmp, struct inode, i_list);
- truncate_inode_pages(inode, 0);
+ clear_inode(inode);
+ count++;
}
/* Add them all to the unused list in one fell swoop */
spin_lock(&inode_lock);
list_splice(head, &inode_unused);
+ inodes_stat.nr_free_inodes += count;
spin_unlock(&inode_lock);
}
@@ -326,25 +303,25 @@ int invalidate_inodes(struct super_block * sb)
}
/*
- * This is called with the inode lock held. It just looks at the last
- * inode on the in-use list, and if the inode is trivially freeable
- * we just move it to the unused list.
- *
- * Otherwise we just move the inode to be the first inode and expect to
- * get back to the problem later..
+ * This is called with the inode lock held. It searches
+ * the in-use for the specified number of freeable inodes.
+ * Freeable inodes are moved to a temporary list and then
+ * placed on the unused list by dispose_list.
+ *
+ * N.B. The spinlock is released to call dispose_list.
*/
#define CAN_UNUSE(inode) \
(((inode)->i_count == 0) && \
- ((inode)->i_nrpages == 0) && \
(!(inode)->i_state))
-static void try_to_free_inodes(void)
+static void try_to_free_inodes(int goal)
{
struct list_head * tmp;
struct list_head *head = &inode_in_use;
+ LIST_HEAD(freeable);
+ int found = 0, search = goal << 1;
- tmp = head->prev;
- if (tmp != head) {
+ while ((tmp = head->prev) != head && search--) {
struct inode * inode;
list_del(tmp);
@@ -352,13 +329,81 @@ static void try_to_free_inodes(void)
if (CAN_UNUSE(inode)) {
list_del(&inode->i_hash);
INIT_LIST_HEAD(&inode->i_hash);
- head = &inode_unused;
+ list_add(tmp, &freeable);
+ if (++found < goal)
+ continue;
+ break;
}
list_add(tmp, head);
}
+ /*
+ * If we didn't free any inodes, do a limited
+ * pruning of the dcache to help the next time.
+ */
+ spin_unlock(&inode_lock);
+ if (found)
+ dispose_list(&freeable);
+ else
+ prune_dcache(goal);
+ spin_lock(&inode_lock);
+}
+
+/*
+ * This is called with the spinlock held, but releases
+ * the lock when freeing or allocating inodes.
+ * Look out! This returns with the inode lock held if
+ * it got an inode..
+ */
+static struct inode * grow_inodes(void)
+{
+ struct inode * inode;
+
+ /*
+ * Check whether to shrink the dcache ... if we've
+ * allocated more than half of the nominal maximum,
+ * try shrinking before allocating more.
+ */
+ if (inodes_stat.nr_inodes >= (max_inodes >> 1)) {
+ struct list_head * tmp;
+
+ spin_unlock(&inode_lock);
+ prune_dcache(128);
+ spin_lock(&inode_lock);
+ try_to_free_inodes(128);
+ tmp = inode_unused.next;
+ if (tmp != &inode_unused) {
+ inodes_stat.nr_free_inodes--;
+ list_del(tmp);
+ inode = list_entry(tmp, struct inode, i_list);
+ return inode;
+ }
+ }
+
+ spin_unlock(&inode_lock);
+ inode = (struct inode *)__get_free_page(GFP_KERNEL);
+ if (inode) {
+ int size;
+ struct inode * tmp;
+
+ spin_lock(&inode_lock);
+ size = PAGE_SIZE - 2*sizeof(struct inode);
+ tmp = inode;
+ do {
+ tmp++;
+ init_once(tmp);
+ list_add(&tmp->i_list, &inode_unused);
+ inodes_stat.nr_free_inodes++;
+ size -= sizeof(struct inode);
+ } while (size >= 0);
+ init_once(inode);
+ inodes_stat.nr_inodes += PAGE_SIZE / sizeof(struct inode);
+ }
+ return inode;
}
-
+/*
+ * Called with the inode lock held.
+ */
static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head)
{
struct list_head *tmp;
@@ -416,17 +461,22 @@ struct inode * get_empty_inode(void)
struct list_head * tmp;
spin_lock(&inode_lock);
- try_to_free_inodes();
+ /*
+ * Check whether to restock the unused list.
+ */
+ if (inodes_stat.nr_free_inodes < 16)
+ try_to_free_inodes(8);
tmp = inode_unused.next;
if (tmp != &inode_unused) {
list_del(tmp);
+ inodes_stat.nr_free_inodes--;
inode = list_entry(tmp, struct inode, i_list);
add_new_inode:
+ list_add(&inode->i_list, &inode_in_use);
inode->i_sb = NULL;
inode->i_dev = 0;
inode->i_ino = ++last_ino;
inode->i_count = 1;
- list_add(&inode->i_list, &inode_in_use);
inode->i_state = 0;
spin_unlock(&inode_lock);
clean_inode(inode);
@@ -437,7 +487,6 @@ add_new_inode:
* Warning: if this succeeded, we will now
* return with the inode lock.
*/
- spin_unlock(&inode_lock);
inode = grow_inodes();
if (inode)
goto add_new_inode;
@@ -455,6 +504,7 @@ static struct inode * get_new_inode(struct super_block *sb, unsigned long ino, s
if (tmp != &inode_unused) {
list_del(tmp);
+ inodes_stat.nr_free_inodes--;
inode = list_entry(tmp, struct inode, i_list);
add_new_inode:
list_add(&inode->i_list, &inode_in_use);
@@ -477,20 +527,18 @@ add_new_inode:
* state of the inode when it is locked, as we
* just created it (so there can be no old holders
* that haven't tested I_LOCK).
- *
- * Verify this some day!
*/
inode->i_state &= ~I_LOCK;
+ wake_up(&inode->i_wait);
return inode;
}
/*
- * Uhhuh.. We need to expand. Unlock for the allocation,
- * but note that "grow_inodes()" will return with the
- * lock held again if the allocation succeeded.
+ * Uhhuh.. We need to expand. Note that "grow_inodes()" will
+ * release the spinlock, but will return with the lock held
+ * again if the allocation succeeded.
*/
- spin_unlock(&inode_lock);
inode = grow_inodes();
if (inode) {
/* We released the lock, so.. */
@@ -498,6 +546,7 @@ add_new_inode:
if (!old)
goto add_new_inode;
list_add(&inode->i_list, &inode_unused);
+ inodes_stat.nr_free_inodes++;
spin_unlock(&inode_lock);
wait_on_inode(old);
return old;
@@ -518,14 +567,25 @@ struct inode *iget(struct super_block *sb, unsigned long ino)
struct inode * inode;
spin_lock(&inode_lock);
+ if (!inodes_stat.nr_free_inodes)
+ goto restock;
+search:
inode = find_inode(sb, ino, head);
if (!inode) {
- try_to_free_inodes();
return get_new_inode(sb, ino, head);
}
spin_unlock(&inode_lock);
wait_on_inode(inode);
return inode;
+
+ /*
+ * We restock the freelist before calling find,
+ * in order to avoid repeating the search.
+ * (The unused list usually won't be empty.)
+ */
+restock:
+ try_to_free_inodes(8);
+ goto search;
}
void insert_inode_hash(struct inode *inode)
@@ -560,9 +620,18 @@ void iput(struct inode *inode)
}
if (list_empty(&inode->i_hash)) {
list_del(&inode->i_list);
+ INIT_LIST_HEAD(&inode->i_list);
+ spin_unlock(&inode_lock);
+ clear_inode(inode);
+ spin_lock(&inode_lock);
list_add(&inode->i_list, &inode_unused);
+ inodes_stat.nr_free_inodes++;
}
}
+ if (inode->i_count > (1<<15)) {
+ printk("iput: device %s inode %ld count wrapped\n",
+ kdevname(inode->i_dev), inode->i_ino);
+ }
spin_unlock(&inode_lock);
}
}
diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c
index 98814d220..ad70f7b55 100644
--- a/fs/lockd/clntlock.c
+++ b/fs/lockd/clntlock.c
@@ -71,7 +71,7 @@ nlmclnt_block(struct nlm_host *host, struct file_lock *fl, u32 *statp)
* nlmclnt_lock for an explanation.
*/
current->timeout = jiffies + 30 * HZ;
- interruptible_sleep_on(&block.b_wait);
+ sleep_on(&block.b_wait);
for (head = &nlm_blocked; *head; head = &(*head)->b_next) {
if (*head == &block) {
diff --git a/fs/locks.c b/fs/locks.c
index 909c2eacb..738fa0bca 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -132,7 +132,9 @@ static int flock_lock_file(struct file *filp, struct file_lock *caller,
static int posix_locks_deadlock(struct file_lock *caller,
struct file_lock *blocker);
-static struct file_lock *locks_alloc_lock(struct file_lock *fl);
+static struct file_lock *locks_empty_lock(void);
+static struct file_lock *locks_init_lock(struct file_lock *,
+ struct file_lock *);
static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl);
static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait);
static char *lock_get_status(struct file_lock *fl, int id, char *pfx);
@@ -143,6 +145,15 @@ static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait);
struct file_lock *file_lock_table = NULL;
+/* Allocate a new lock, and initialize its fields from fl.
+ * The lock is not inserted into any lists until locks_insert_lock() or
+ * locks_insert_block() are called.
+ */
+static inline struct file_lock *locks_alloc_lock(struct file_lock *fl)
+{
+ return locks_init_lock(locks_empty_lock(), fl);
+}
+
/* Free lock not inserted in any queue.
*/
static inline void locks_free_lock(struct file_lock *fl)
@@ -250,6 +261,7 @@ static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait)
struct file_lock *waiter;
while ((waiter = blocker->fl_nextblock) != NULL) {
+ /* N.B. Is it possible for the notify function to block?? */
if (waiter->fl_notify)
waiter->fl_notify(waiter);
wake_up(&waiter->fl_wait);
@@ -322,7 +334,7 @@ int fcntl_getlk(unsigned int fd, struct flock *l)
return -EINVAL;
if (filp->f_op->lock) {
- error = filp->f_op->lock(inode, filp, F_GETLK, &file_lock);
+ error = filp->f_op->lock(filp, F_GETLK, &file_lock);
if (error < 0)
return error;
fl = &file_lock;
@@ -330,6 +342,7 @@ int fcntl_getlk(unsigned int fd, struct flock *l)
fl = posix_test_lock(filp, &file_lock);
}
+ flock.l_type = F_UNLCK;
if (fl != NULL) {
flock.l_pid = fl->fl_pid;
flock.l_start = fl->fl_start;
@@ -337,10 +350,7 @@ int fcntl_getlk(unsigned int fd, struct flock *l)
fl->fl_end - fl->fl_start + 1;
flock.l_whence = 0;
flock.l_type = fl->fl_type;
- return (copy_to_user(l, &flock, sizeof(flock)) ? -EFAULT : 0);
- } else {
- flock.l_type = F_UNLCK;
- }
+ }
return (copy_to_user(l, &flock, sizeof(flock)) ? -EFAULT : 0);
}
@@ -368,7 +378,12 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
if (!(inode = dentry->d_inode))
return -EINVAL;
-
+ /*
+ * This might block, so we do it before checking the inode.
+ */
+ if (copy_from_user(&flock, l, sizeof(flock)))
+ return (-EFAULT);
+
/* Don't allow mandatory locks on files that may be memory mapped
* and shared.
*/
@@ -382,8 +397,6 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
} while ((vma = vma->vm_next_share) != NULL);
}
- if (copy_from_user(&flock, l, sizeof(flock)))
- return (-EFAULT);
if (!posix_make_lock(filp, &file_lock, &flock))
return (-EINVAL);
@@ -420,7 +433,7 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
}
if (filp->f_op->lock != NULL) {
- error = filp->f_op->lock(inode, filp, cmd, &file_lock);
+ error = filp->f_op->lock(filp, cmd, &file_lock);
if (error < 0)
return (error);
}
@@ -441,8 +454,8 @@ void locks_remove_locks(struct task_struct *task, struct file *filp)
* close on that file.
*/
inode = filp->f_dentry->d_inode;
+repeat:
before = &inode->i_flock;
-
while ((fl = *before) != NULL) {
if (((fl->fl_flags & FL_POSIX) && (fl->fl_owner == task)) ||
((fl->fl_flags & FL_FLOCK) && (fl->fl_file == filp) &&
@@ -451,13 +464,13 @@ void locks_remove_locks(struct task_struct *task, struct file *filp)
locks_delete_lock(before, 0);
if (filp->f_op->lock) {
file_lock.fl_type = F_UNLCK;
- filp->f_op->lock(inode, filp, F_SETLK, &file_lock);
+ filp->f_op->lock(filp, F_SETLK, &file_lock);
/* List may have changed: */
- before = &inode->i_flock;
+ goto repeat;
}
- } else {
- before = &fl->fl_next;
+ continue;
}
+ before = &fl->fl_next;
}
return;
@@ -762,16 +775,30 @@ static int flock_lock_file(struct file *filp, struct file_lock *caller,
unsigned int wait)
{
struct file_lock *fl;
- struct file_lock *new_fl;
+ struct file_lock *new_fl = NULL;
struct file_lock **before;
struct inode * inode = filp->f_dentry->d_inode;
- int change = 0;
+ int error, change;
+ int unlock = (caller->fl_type == F_UNLCK);
+
+ /*
+ * If we need a new lock, get it in advance to avoid races.
+ */
+ if (!unlock) {
+ error = -ENOLCK;
+ new_fl = locks_alloc_lock(caller);
+ if (!new_fl)
+ goto out;
+ }
+ error = 0;
+search:
+ change = 0;
before = &inode->i_flock;
while (((fl = *before) != NULL) && (fl->fl_flags & FL_FLOCK)) {
if (caller->fl_file == fl->fl_file) {
if (caller->fl_type == fl->fl_type)
- return (0);
+ goto out;
change = 1;
break;
}
@@ -780,44 +807,43 @@ static int flock_lock_file(struct file *filp, struct file_lock *caller,
/* change means that we are changing the type of an existing lock, or
* or else unlocking it.
*/
- if (change)
- locks_delete_lock(before, caller->fl_type != F_UNLCK);
- if (caller->fl_type == F_UNLCK)
- return (0);
- if ((new_fl = locks_alloc_lock(caller)) == NULL)
- return (-ENOLCK);
+ if (change) {
+ /* N.B. What if the wait argument is false? */
+ locks_delete_lock(before, !unlock);
+ /*
+ * If we waited, another lock may have been added ...
+ */
+ if (!unlock)
+ goto search;
+ }
+ if (unlock)
+ goto out;
+
repeat:
+ /* Check signals each time we start */
+ error = -ERESTARTSYS;
+ if (current->signal & ~current->blocked)
+ goto out;
for (fl = inode->i_flock; (fl != NULL) && (fl->fl_flags & FL_FLOCK);
fl = fl->fl_next) {
if (!flock_locks_conflict(new_fl, fl))
continue;
- if (!wait) {
- locks_free_lock(new_fl);
- return (-EAGAIN);
- }
- if (current->signal & ~current->blocked) {
- /* Note: new_fl is not in any queue at this
- * point, so we must use locks_free_lock()
- * instead of locks_delete_lock()
- * Dmitry Gorodchanin 09/02/96.
- */
- locks_free_lock(new_fl);
- return (-ERESTARTSYS);
- }
+ error = -EAGAIN;
+ if (!wait)
+ goto out;
locks_insert_block(fl, new_fl);
interruptible_sleep_on(&new_fl->fl_wait);
locks_delete_block(fl, new_fl);
- if (current->signal & ~current->blocked) {
- /* Awakened by a signal. Free the new
- * lock and return an error.
- */
- locks_free_lock(new_fl);
- return (-ERESTARTSYS);
- }
goto repeat;
}
locks_insert_lock(&inode->i_flock, new_fl);
- return (0);
+ new_fl = NULL;
+ error = 0;
+
+out:
+ if (new_fl)
+ locks_free_lock(new_fl);
+ return error;
}
/* Add a POSIX style lock to a file.
@@ -836,36 +862,51 @@ int posix_lock_file(struct file *filp, struct file_lock *caller,
unsigned int wait)
{
struct file_lock *fl;
- struct file_lock *new_fl;
+ struct file_lock *new_fl, *new_fl2;
struct file_lock *left = NULL;
struct file_lock *right = NULL;
struct file_lock **before;
struct inode * inode = filp->f_dentry->d_inode;
- int added = 0;
+ int error, added = 0;
+
+ /*
+ * We may need two file_lock structures for this operation,
+ * so we get them in advance to avoid races.
+ */
+ new_fl = locks_empty_lock();
+ new_fl2 = locks_empty_lock();
+ error = -ENOLCK; /* "no luck" */
+ if (!(new_fl && new_fl2))
+ goto out;
if (caller->fl_type != F_UNLCK) {
repeat:
+ error = -ERESTARTSYS;
+ if (current->signal & ~current->blocked)
+ goto out;
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
if (!(fl->fl_flags & FL_POSIX))
continue;
if (!posix_locks_conflict(caller, fl))
continue;
+ error = -EAGAIN;
if (!wait)
- return (-EAGAIN);
- if (current->signal & ~current->blocked)
- return (-ERESTARTSYS);
+ goto out;
+ error = -EDEADLK;
if (posix_locks_deadlock(caller, fl))
- return (-EDEADLK);
+ goto out;
locks_insert_block(fl, caller);
interruptible_sleep_on(&caller->fl_wait);
locks_delete_block(fl, caller);
- if (current->signal & ~current->blocked)
- return (-ERESTARTSYS);
goto repeat;
}
}
- /* Find the first old lock with the same owner as the new lock.
+ /*
+ * We've allocated the new locks in advance, so there are no
+ * errors possible (and no blocking operations) from here on.
+ *
+ * Find the first old lock with the same owner as the new lock.
*/
before = &inode->i_flock;
@@ -958,25 +999,23 @@ int posix_lock_file(struct file *filp, struct file_lock *caller,
before = &fl->fl_next;
}
+ error = 0;
if (!added) {
if (caller->fl_type == F_UNLCK)
- return (0);
- if ((new_fl = locks_alloc_lock(caller)) == NULL)
- return (-ENOLCK);
+ goto out;
+ locks_init_lock(new_fl, caller);
locks_insert_lock(before, new_fl);
+ new_fl = NULL;
}
if (right) {
if (left == right) {
- /* The new lock breaks the old one in two pieces, so we
- * have to allocate one more lock (in this case, even
- * F_UNLCK may fail!).
+ /* The new lock breaks the old one in two pieces,
+ * so we have to use the second new lock (in this
+ * case, even F_UNLCK may fail!).
*/
- if ((left = locks_alloc_lock(right)) == NULL) {
- if (!added)
- locks_delete_lock(before, 0);
- return (-ENOLCK);
- }
+ left = locks_init_lock(new_fl2, right);
locks_insert_lock(before, left);
+ new_fl2 = NULL;
}
right->fl_start = caller->fl_end + 1;
locks_wake_up_blocks(right, 0);
@@ -985,35 +1024,48 @@ int posix_lock_file(struct file *filp, struct file_lock *caller,
left->fl_end = caller->fl_start - 1;
locks_wake_up_blocks(left, 0);
}
- return (0);
+out:
+ /*
+ * Free any unused locks. (They haven't
+ * ever been used, so we use kfree().)
+ */
+ if (new_fl)
+ kfree(new_fl);
+ if (new_fl2)
+ kfree(new_fl2);
+ return error;
}
-/* Allocate new lock.
- * Initialize its fields from fl. The lock is not inserted into any
- * lists until locks_insert_lock() or locks_insert_block() are called.
+/*
+ * Allocate an empty lock structure. We can use GFP_KERNEL now that
+ * all allocations are done in advance.
*/
-static struct file_lock *locks_alloc_lock(struct file_lock *fl)
+static struct file_lock *locks_empty_lock(void)
{
- struct file_lock *tmp;
-
/* Okay, let's make a new file_lock structure... */
- if ((tmp = (struct file_lock *)kmalloc(sizeof(struct file_lock),
- GFP_ATOMIC)) == NULL)
- return (tmp);
-
- memset(tmp, 0, sizeof(*tmp));
-
- tmp->fl_flags = fl->fl_flags;
- tmp->fl_owner = fl->fl_owner;
- tmp->fl_pid = fl->fl_pid;
- tmp->fl_file = fl->fl_file;
- tmp->fl_type = fl->fl_type;
- tmp->fl_start = fl->fl_start;
- tmp->fl_end = fl->fl_end;
- tmp->fl_notify = fl->fl_notify;
- tmp->fl_u = fl->fl_u;
-
- return (tmp);
+ return ((struct file_lock *) kmalloc(sizeof(struct file_lock),
+ GFP_KERNEL));
+}
+
+/*
+ * Initialize a new lock from an existing file_lock structure.
+ */
+static struct file_lock *locks_init_lock(struct file_lock *new,
+ struct file_lock *fl)
+{
+ if (new) {
+ memset(new, 0, sizeof(*new));
+ new->fl_owner = fl->fl_owner;
+ new->fl_pid = fl->fl_pid;
+ new->fl_file = fl->fl_file;
+ new->fl_flags = fl->fl_flags;
+ new->fl_type = fl->fl_type;
+ new->fl_start = fl->fl_start;
+ new->fl_end = fl->fl_end;
+ new->fl_notify = fl->fl_notify;
+ new->fl_u = fl->fl_u;
+ }
+ return new;
}
/* Insert file lock fl into an inode's lock list at the position indicated
diff --git a/fs/minix/fsync.c b/fs/minix/fsync.c
index 9510d6364..44606d260 100644
--- a/fs/minix/fsync.c
+++ b/fs/minix/fsync.c
@@ -328,10 +328,14 @@ int V2_minix_sync_file(struct inode * inode, struct file * file)
}
/*
- * The function which is called for file synchronization.
+ * The function which is called for file synchronization. File may be
+ * NULL
*/
-int minix_sync_file(struct inode * inode, struct file * file)
+
+int minix_sync_file(struct file * file, struct dentry *dentry)
{
+ struct inode *inode = dentry->d_inode;
+
if (INODE_VERSION(inode) == MINIX_V1)
return V1_minix_sync_file(inode, file);
else
diff --git a/fs/namei.c b/fs/namei.c
index 5aa6a5422..54714754d 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -593,8 +593,12 @@ struct dentry * open_namei(const char * pathname, int flag, int mode)
* An append-only file must be opened in append mode for writing.
*/
error = -EPERM;
- if (IS_APPEND(inode) && ((flag & FMODE_WRITE) && !(flag & O_APPEND)))
- goto exit;
+ if (IS_APPEND(inode)) {
+ if ((flag & FMODE_WRITE) && !(flag & O_APPEND))
+ goto exit;
+ if (flag & O_TRUNC)
+ goto exit;
+ }
if (flag & O_TRUNC) {
error = get_write_access(inode);
diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c
index 9a302a213..9a2067848 100644
--- a/fs/ncpfs/file.c
+++ b/fs/ncpfs/file.c
@@ -24,7 +24,7 @@ static inline int min(int a, int b)
return a < b ? a : b;
}
-static int ncp_fsync(struct inode *inode, struct file *file)
+static int ncp_fsync(struct file *file, struct dentry *dentry)
{
return 0;
}
diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c
index 269d3ef07..5dfdeb27f 100644
--- a/fs/ncpfs/mmap.c
+++ b/fs/ncpfs/mmap.c
@@ -116,8 +116,10 @@ struct vm_operations_struct ncp_file_mmap =
/* This is used for a general mmap of a ncp file */
-int ncp_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma)
+int ncp_mmap(struct file *file, struct vm_area_struct *vma)
{
+ struct inode *inode = file->f_dentry->d_inode;
+
DPRINTK("ncp_mmap: called\n");
if (!ncp_conn_valid(NCP_SERVER(inode))) {
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index eb4735a6d..4587950ef 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -32,13 +32,12 @@
#define NFSDBG_FACILITY NFSDBG_FILE
-static int nfs_file_mmap(struct inode *, struct file *,
- struct vm_area_struct *);
+static int nfs_file_mmap(struct file *, struct vm_area_struct *);
static long nfs_file_read(struct inode *, struct file *, char *, unsigned long);
static long nfs_file_write(struct inode *, struct file *,
const char *, unsigned long);
static int nfs_file_close(struct inode *, struct file *);
-static int nfs_fsync(struct inode *, struct file *);
+static int nfs_fsync(struct file *, struct dentry *dentry);
static struct file_operations nfs_file_operations = {
NULL, /* lseek - default */
@@ -114,20 +113,21 @@ nfs_file_read(struct inode * inode, struct file * file,
}
static int
-nfs_file_mmap(struct inode * inode, struct file * file,
- struct vm_area_struct * vma)
+nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
{
int status;
+ struct inode *inode = file->f_dentry->d_inode;
dfprintk(VFS, "nfs: mmap(%x/%ld)\n", inode->i_dev, inode->i_ino);
if ((status = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0)
return status;
- return generic_file_mmap(inode, file, vma);
+ return generic_file_mmap(file, vma);
}
-static int nfs_fsync(struct inode *inode, struct file *file)
+static int nfs_fsync(struct file *file, struct dentry *dentry)
{
+ struct inode *inode = dentry->d_inode;
dfprintk(VFS, "nfs: fsync(%x/%ld)\n", inode->i_dev, inode->i_ino);
return nfs_flush_dirty_pages(inode, 0, 0);
@@ -175,9 +175,10 @@ nfs_file_write(struct inode *inode, struct file *file,
* Lock a (portion of) a file
*/
int
-nfs_lock(struct inode *inode, struct file *filp, int cmd, struct file_lock *fl)
+nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
{
int status;
+ struct inode * inode;
dprintk("NFS: nfs_lock(f=%4x/%ld, t=%x, fl=%x, r=%ld:%ld)\n",
filp->f_dentry->d_inode->i_dev, filp->f_dentry->d_inode->i_ino,
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 449fc0d4e..ec5a1f7be 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -56,6 +56,16 @@
#include <linux/nfs_fs.h>
#include <asm/uaccess.h>
+/*
+ * NOTE! We must NOT default to soft-mounting: that breaks too many
+ * programs that depend on POSIX behaviour of uninterruptible reads
+ * and writes.
+ *
+ * Until we have a per-mount soft/hard mount policy that we can honour
+ * we must default to hard mounting!
+ */
+#define IS_SOFT 0
+
#define NFSDBG_FACILITY NFSDBG_PAGECACHE
static void nfs_wback_lock(struct rpc_task *task);
@@ -397,19 +407,24 @@ wait_on_write_request(struct nfs_wreq *req)
{
struct wait_queue wait = { current, NULL };
struct page *page = req->wb_page;
+ int retval;
add_wait_queue(&page->wait, &wait);
atomic_inc(&page->count);
-repeat:
- current->state = TASK_INTERRUPTIBLE;
- if (PageLocked(page)) {
+ for (;;) {
+ current->state = IS_SOFT ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE;
+ retval = 0;
+ if (!PageLocked(page))
+ break;
+ retval = -ERESTARTSYS;
+ if (IS_SOFT && signalled())
+ break;
schedule();
- goto repeat;
}
remove_wait_queue(&page->wait, &wait);
current->state = TASK_RUNNING;
atomic_dec(&page->count);
- return signalled()? -ERESTARTSYS : 0;
+ return retval;
}
/*
@@ -613,10 +628,13 @@ nfs_flush_dirty_pages(struct inode *inode, off_t offset, off_t len)
inode->i_dev, inode->i_ino, current->pid,
offset, len);
- if (signalled())
+ if (IS_SOFT && signalled())
nfs_cancel_dirty(inode, current->pid);
- while (!signalled()) {
+ for (;;) {
+ if (IS_SOFT && signalled())
+ return -ERESTARTSYS;
+
/* Flush all pending writes for this pid and file region */
last = nfs_flush_pages(inode, current->pid, offset, len, 0);
if (last == NULL)
@@ -624,7 +642,7 @@ nfs_flush_dirty_pages(struct inode *inode, off_t offset, off_t len)
wait_on_write_request(last);
}
- return signalled()? -ERESTARTSYS : 0;
+ return 0;
}
/*
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index dbbfbc78f..df6eb0e2c 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -294,7 +294,7 @@ nfsd_close(struct file *filp)
void
nfsd_sync(struct inode *inode, struct file *filp)
{
- filp->f_op->fsync(inode, filp);
+ filp->f_op->fsync(filp, filp->f_dentry);
}
/*
@@ -928,7 +928,8 @@ 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, &cd, (filldir_t) func);
+ err = file.f_op->readdir(&file,
+ &cd, (filldir_t) func);
if (err < 0) {
nfsd_close(&file);
diff --git a/fs/open.c b/fs/open.c
index 82fb66caf..944186e68 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -717,7 +717,7 @@ int __fput(struct file *filp)
struct inode * inode = dentry->d_inode;
if (filp->f_op && filp->f_op->release)
- error = filp->f_op->release(inode,filp);
+ error = filp->f_op->release(inode, filp);
filp->f_dentry = NULL;
if (filp->f_mode & FMODE_WRITE)
put_write_access(inode);
diff --git a/fs/pipe.c b/fs/pipe.c
index f76157c7b..f5688541d 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -133,8 +133,7 @@ static long pipe_write(struct inode * inode, struct file * filp,
return written;
}
-static long long pipe_lseek(struct inode * inode, struct file * file,
- long long offset, int orig)
+static long long pipe_lseek(struct file * file, long long offset, int orig)
{
return -ESPIPE;
}
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 358060020..83139f492 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -20,8 +20,7 @@ static long proc_file_read(struct inode * inode, struct file * file,
char * buf, unsigned long nbytes);
static long proc_file_write(struct inode * inode, struct file * file,
const char * buffer, unsigned long count);
-static long long proc_file_lseek(struct inode * inode, struct file * file,
- long long offset, int orig);
+static long long proc_file_lseek(struct file * file, long long offset, int orig);
int proc_match(int len, const char *name,struct proc_dir_entry * de)
{
@@ -190,8 +189,7 @@ proc_file_write(struct inode * inode, struct file * file,
}
-static long long proc_file_lseek(struct inode * inode, struct file * file,
- long long offset, int orig)
+static long long proc_file_lseek(struct file * file, long long offset, int orig)
{
switch (orig) {
case 0:
diff --git a/fs/proc/mem.c b/fs/proc/mem.c
index 97acb5ee8..126683ea4 100644
--- a/fs/proc/mem.c
+++ b/fs/proc/mem.c
@@ -187,8 +187,7 @@ static long mem_write(struct inode * inode, struct file * file,
#endif
-static long long mem_lseek(struct inode * inode, struct file * file,
- long long offset, int orig)
+static long long mem_lseek(struct file * file, long long offset, int orig)
{
switch (orig) {
case 0:
@@ -205,8 +204,7 @@ static long long mem_lseek(struct inode * inode, struct file * file,
/*
* This isn't really reliable by any means..
*/
-int mem_mmap(struct inode * inode, struct file * file,
- struct vm_area_struct * vma)
+int mem_mmap(struct file * file, struct vm_area_struct * vma)
{
struct task_struct *tsk;
pgd_t *src_dir, *dest_dir;
@@ -214,7 +212,8 @@ int mem_mmap(struct inode * inode, struct file * file,
pte_t *src_table, *dest_table;
unsigned long stmp, dtmp;
struct vm_area_struct *src_vma = NULL;
-
+ struct inode *inode = file->f_dentry->d_inode;
+
/* Get the source's task information */
tsk = get_task(inode->i_ino >> 16);
diff --git a/fs/proc/scsi.c b/fs/proc/scsi.c
index b1e77398c..831a2623d 100644
--- a/fs/proc/scsi.c
+++ b/fs/proc/scsi.c
@@ -33,7 +33,7 @@ static long proc_readscsi(struct inode * inode, struct file * file,
char * buf, unsigned long count);
static long proc_writescsi(struct inode * inode, struct file * file,
const char * buf, unsigned long count);
-static long long proc_scsilseek(struct inode *, struct file *, long long, int);
+static long long proc_scsilseek(struct file *, long long, int);
extern void build_proc_dir_hba_entries(uint);
@@ -177,8 +177,7 @@ static long proc_writescsi(struct inode * inode, struct file * file,
}
-static long long proc_scsilseek(struct inode * inode, struct file * file,
- long long offset, int orig)
+static long long proc_scsilseek(struct file * file, long long offset, int orig)
{
switch (orig) {
case 0:
diff --git a/fs/read_write.c b/fs/read_write.c
index 6d31c7af0..df3d34d3e 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -19,16 +19,13 @@
#include <asm/uaccess.h>
-static long long default_llseek(struct inode *inode,
- struct file *file,
- long long offset,
- int origin)
+static long long default_llseek(struct file *file, long long offset, int origin)
{
long long retval;
switch (origin) {
case 2:
- offset += inode->i_size;
+ offset += file->f_dentry->d_inode->i_size;
break;
case 1:
offset += file->f_pos;
@@ -45,15 +42,14 @@ static long long default_llseek(struct inode *inode,
return retval;
}
-static inline long long llseek(struct inode * inode, struct file *file,
- long long offset, unsigned int origin)
+static inline long long llseek(struct file *file, long long offset, unsigned int origin)
{
- long long (*fn)(struct inode *, struct file *, long long, int);
+ long long (*fn)(struct file *, long long, int);
fn = default_llseek;
if (file->f_op && file->f_op->llseek)
fn = file->f_op->llseek;
- return fn(inode, file, offset, origin);
+ return fn(file, offset, origin);
}
asmlinkage long sys_lseek(unsigned int fd, off_t offset, unsigned int origin)
@@ -73,7 +69,7 @@ asmlinkage long sys_lseek(unsigned int fd, off_t offset, unsigned int origin)
retval = -EINVAL;
if (origin > 2)
goto bad;
- retval = llseek(inode, file, offset, origin);
+ retval = llseek(file, offset, origin);
bad:
unlock_kernel();
return retval;
@@ -100,8 +96,7 @@ asmlinkage int sys_llseek(unsigned int fd, unsigned long offset_high,
if (origin > 2)
goto bad;
- offset = llseek(inode, file,
- (((unsigned long long) offset_high << 32) | offset_low),
+ offset = llseek(file, (((unsigned long long) offset_high << 32) | offset_low),
origin);
retval = offset;
diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c
index 2b0c4d1e2..3196e9009 100644
--- a/fs/smbfs/dir.c
+++ b/fs/smbfs/dir.c
@@ -19,6 +19,12 @@
#include <asm/uaccess.h>
#include <asm/semaphore.h>
+#define SMBFS_PARANOIA 1
+/* #define SMBFS_DEBUG_VERBOSE 1 */
+/* #define pr_debug printk */
+
+#define this_dir_cached(dir) ((dir->i_sb == c_sb) && (dir->i_ino == c_ino))
+
static long
smb_dir_read(struct inode *inode, struct file *filp,
char *buf, unsigned long count);
@@ -69,6 +75,15 @@ struct inode_operations smb_dir_inode_operations =
NULL /* smap */
};
+static void smb_put_dentry(struct dentry *);
+static struct dentry_operations smbfs_dentry_operations =
+{
+ NULL, /* revalidate */
+ NULL, /* d_hash */
+ NULL, /* d_compare */
+ smb_put_dentry /* d_delete */
+};
+
static long
smb_dir_read(struct inode *inode, struct file *filp, char *buf,
unsigned long count)
@@ -76,166 +91,240 @@ smb_dir_read(struct inode *inode, struct file *filp, char *buf,
return -EISDIR;
}
+/*
+ * This is the callback from dput(). We close the file so that
+ * cached dentries don't keep the file open.
+ */
+void
+smb_put_dentry(struct dentry *dentry)
+{
+ struct inode *ino = dentry->d_inode;
+ if (ino)
+ smb_close(ino);
+}
+
+/* Static variables for the dir cache */
+static struct smb_dirent *c_entry = NULL;
+static struct super_block * c_sb = NULL;
static unsigned long c_ino = 0;
-static kdev_t c_dev;
-static int c_size;
static int c_seen_eof;
+static int c_size;
static int c_last_returned_index;
-static struct smb_dirent *c_entry = NULL;
static struct smb_dirent *
smb_search_in_cache(struct inode *dir, unsigned long f_pos)
{
int i;
- if ((dir->i_dev != c_dev) || (dir->i_ino != c_ino))
- {
- return NULL;
- }
- for (i = 0; i < c_size; i++)
- {
- if (f_pos == c_entry[i].f_pos)
+ if (this_dir_cached(dir))
+ for (i = 0; i < c_size; i++)
{
- c_last_returned_index = i;
- return &(c_entry[i]);
+ if (c_entry[i].f_pos < f_pos)
+ continue;
+ if (c_entry[i].f_pos == f_pos)
+ {
+ c_last_returned_index = i;
+ return &(c_entry[i]);
+ }
+ break;
}
- }
return NULL;
}
+/*
+ * Compute the hash for a qstr ... move to include/linux/dcache.h?
+ */
+static unsigned int hash_it(const char * name, unsigned int len)
+{
+ unsigned long hash;
+ hash = init_name_hash();
+ while (len--)
+ hash = partial_name_hash(*name++, hash);
+ return end_name_hash(hash);
+}
+
+static struct semaphore refill_cache_sem = MUTEX;
+/*
+ * Called with the refill semaphore held.
+ */
static int
smb_refill_dir_cache(struct dentry *dentry, unsigned long f_pos)
{
- int result;
struct inode *dir = dentry->d_inode;
- static struct semaphore sem = MUTEX;
- int i;
- ino_t ino;
+ ino_t ino_start;
+ int i, result;
- do
+ result = smb_proc_readdir(dentry, f_pos,
+ SMB_READDIR_CACHE_SIZE, c_entry);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_refill_dir_cache: dir=%s/%s, pos=%lu, result=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, f_pos, result);
+#endif
+
+ if (result <= 0)
{
- down(&sem);
- result = smb_proc_readdir(dentry, f_pos,
- SMB_READDIR_CACHE_SIZE, c_entry);
+ /*
+ * If an error occurred, the cache may have been partially
+ * filled prior to failing, so we must invalidate.
+ * N.B. Might not need to for 0 return ... save cache?
+ */
+ c_sb = NULL;
+ c_ino = 0;
+ c_seen_eof = 0;
+ goto out;
+ }
- if (result <= 0)
+ /* Suppose there are a multiple of cache entries? */
+ c_seen_eof = (result < SMB_READDIR_CACHE_SIZE);
+ c_sb = dir->i_sb;
+ c_ino = dir->i_ino;
+ c_size = result;
+ c_last_returned_index = 0; /* is this used? */
+
+ ino_start = smb_invent_inos(c_size);
+ /*
+ * If a dentry already exists, we have to give the cache entry
+ * the correct inode number. This is needed for getcwd().
+ */
+ for (i = 0; i < c_size; i++)
+ {
+ struct dentry * new_dentry;
+ struct qstr qname;
+
+ c_entry[i].attr.f_ino = ino_start++;
+ qname.name = c_entry[i].name;
+ qname.len = c_entry[i].len;
+ qname.hash = hash_it(qname.name, qname.len);
+ new_dentry = d_lookup(dentry, &qname);
+ if (new_dentry)
{
- smb_invalid_dir_cache(dir->i_ino);
- up(&sem);
- return result;
+ struct inode * inode = new_dentry->d_inode;
+ if (inode)
+ c_entry[i].attr.f_ino = inode->i_ino;
+ dput(new_dentry);
}
- c_seen_eof = (result < SMB_READDIR_CACHE_SIZE);
- c_dev = dir->i_dev;
- c_ino = dir->i_ino;
- c_size = result;
- c_last_returned_index = 0;
-
- ino = smb_invent_inos(c_size);
-
- for (i = 0; i < c_size; i++)
- c_entry[i].attr.f_ino = ino++;
-
- up(&sem);
}
- while ((c_dev != dir->i_dev) || (c_ino != dir->i_ino));
-
+out:
return result;
}
-static int smb_readdir(struct file *filp,
- void *dirent, filldir_t filldir)
+static int
+smb_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
struct dentry *dentry = filp->f_dentry;
struct inode *dir = dentry->d_inode;
- int result, i = 0;
- struct smb_dirent *entry = NULL;
+ struct smb_dirent *entry;
+ int result;
pr_debug("smb_readdir: filp->f_pos = %d\n", (int) filp->f_pos);
pr_debug("smb_readdir: dir->i_ino = %ld, c_ino = %ld\n",
dir->i_ino, c_ino);
+ result = -EBADF;
if ((dir == NULL) || !S_ISDIR(dir->i_mode))
- {
- return -EBADF;
- }
+ goto out;
+
+ /*
+ * Check whether the directory cache exists yet
+ */
if (c_entry == NULL)
{
- i = sizeof(struct smb_dirent) * SMB_READDIR_CACHE_SIZE;
- c_entry = (struct smb_dirent *) smb_vmalloc(i);
- if (c_entry == NULL)
+ int size = sizeof(struct smb_dirent) * SMB_READDIR_CACHE_SIZE;
+ result = -ENOMEM;
+ entry = (struct smb_dirent *) smb_vmalloc(size);
+ /*
+ * Somebody else may have allocated the cache,
+ * so we check again to avoid a memory leak.
+ */
+ if (!c_entry)
{
- return -ENOMEM;
+ if (!entry)
+ goto out;
+ c_entry = entry;
+ } else if (entry) {
+ printk("smb_readdir: cache already alloced!\n");
+ smb_vfree(entry);
}
}
- if (filp->f_pos == 0)
- {
- c_ino = 0;
- c_dev = 0;
- c_seen_eof = 0;
-
- if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino) < 0)
- return 0;
- filp->f_pos += 1;
- }
- if (filp->f_pos == 1)
+ result = 0;
+ switch ((unsigned int) filp->f_pos)
{
- if (filldir(dirent, "..", 2, filp->f_pos,
- filp->f_dentry->d_parent->d_inode->i_ino) < 0)
- return 0;
-
- filp->f_pos += 1;
+ case 0:
+ if (filldir(dirent, ".", 1, 0, dir->i_ino) < 0)
+ goto out;
+ filp->f_pos = 1;
+ case 1:
+ if (filldir(dirent, "..", 2, 1,
+ dentry->d_parent->d_inode->i_ino) < 0)
+ goto out;
+ filp->f_pos = 2;
}
- entry = smb_search_in_cache(dir, filp->f_pos);
+ /*
+ * Since filldir() could block if dirent is paged out,
+ * we hold the refill semaphore while using the cache.
+ * N.B. It's possible that the directory could change
+ * between calls to readdir ... what to do??
+ */
+ down(&refill_cache_sem);
+ entry = smb_search_in_cache(dir, filp->f_pos);
if (entry == NULL)
{
- if (c_seen_eof)
+ /* Past the end of _this_ directory? */
+ if (this_dir_cached(dir) && c_seen_eof &&
+ filp->f_pos == c_entry[c_size-1].f_pos + 1)
{
- /* End of directory */
- return 0;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_readdir: eof reached for %s/%s, c_size=%d, pos=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, c_size, (int) filp->f_pos);
+#endif
+ goto up_and_out;
}
result = smb_refill_dir_cache(dentry, filp->f_pos);
if (result <= 0)
- {
- return result;
- }
+ goto up_and_out;
entry = c_entry;
}
+
while (entry < &(c_entry[c_size]))
{
- ino_t ino = entry->attr.f_ino;
-
pr_debug("smb_readdir: entry->name = %s\n", entry->name);
- if (filldir(dirent, entry->name, strlen(entry->name),
- entry->f_pos, ino) < 0)
+ if (filldir(dirent, entry->name, entry->len,
+ entry->f_pos, entry->attr.f_ino) < 0)
break;
-
- if ((dir->i_dev != c_dev) || (dir->i_ino != c_ino)
- || (entry->f_pos != filp->f_pos))
- break;
-
+#if SMBFS_PARANOIA
+/* should never happen */
+if (!this_dir_cached(dir) || (entry->f_pos != filp->f_pos))
+printk("smb_readdir: cache changed!\n");
+#endif
filp->f_pos += 1;
entry += 1;
}
- return 0;
+ result = 0;
+
+up_and_out:
+ up(&refill_cache_sem);
+out:
+ return result;
}
void
smb_init_dir_cache(void)
{
- c_ino = 0;
- c_dev = 0;
c_entry = NULL;
+ c_sb = NULL;
+ c_ino = 0;
+ c_seen_eof = 0;
}
void
-smb_invalid_dir_cache(unsigned long ino)
+smb_invalid_dir_cache(struct inode * dir)
{
- /* FIXME: check for dev as well */
- if (ino == c_ino)
+ if (this_dir_cached(dir))
{
+ c_sb = NULL;
c_ino = 0;
c_seen_eof = 0;
}
@@ -246,6 +335,7 @@ smb_free_dir_cache(void)
{
if (c_entry != NULL)
{
+ /* N.B. can this block?? */
smb_vfree(c_entry);
}
c_entry = NULL;
@@ -256,106 +346,111 @@ smb_lookup(struct inode *dir, struct dentry *d_entry)
{
struct smb_fattr finfo;
struct inode *inode;
- int len = d_entry->d_name.len;
int error;
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk("smb_lookup: inode is NULL or not a directory\n");
- return -ENOENT;
- }
-
- if (len > SMB_MAXNAMELEN)
- return -ENAMETOOLONG;
+ error = -ENAMETOOLONG;
+ if (d_entry->d_name.len > SMB_MAXNAMELEN)
+ goto out;
- error = smb_proc_getattr(d_entry, &(d_entry->d_name), &finfo);
+ error = smb_proc_getattr(d_entry->d_parent, &(d_entry->d_name), &finfo);
+#if SMBFS_PARANOIA
+if (error && error != -ENOENT)
+printk("smb_lookup: find %s/%s failed, error=%d\n",
+d_entry->d_parent->d_name.name, d_entry->d_name.name, error);
+#endif
inode = NULL;
- if (!error) {
- error = -ENOENT;
+ if (error == -ENOENT)
+ goto add_entry;
+ if (!error)
+ {
finfo.f_ino = smb_invent_inos(1);
inode = smb_iget(dir->i_sb, &finfo);
- if (!inode)
- return -EACCES;
- } else if (error != -ENOENT)
- return error;
-
- d_add(d_entry, inode);
- return 0;
+ error = -EACCES;
+ if (inode)
+ {
+ /* cache the dentry pointer */
+ inode->u.smbfs_i.dentry = d_entry;
+ add_entry:
+ d_entry->d_op = &smbfs_dentry_operations;
+ d_add(d_entry, inode);
+ error = 0;
+ }
+ }
+out:
+ return error;
}
-static int smb_create(struct inode *dir, struct dentry *dentry, int mode)
+/*
+ * This code is common to all routines creating a new inode.
+ */
+static int
+smb_instantiate(struct inode *dir, struct dentry *dentry)
{
struct smb_fattr fattr;
- struct inode *inode;
int error;
- if (!dir || !S_ISDIR(dir->i_mode))
+ smb_invalid_dir_cache(dir);
+
+ error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &fattr);
+ if (!error)
{
- printk("smb_create: inode is NULL or not a directory\n");
- return -ENOENT;
+ struct inode *inode;
+ error = -EACCES;
+ fattr.f_ino = smb_invent_inos(1);
+ inode = smb_iget(dir->i_sb, &fattr);
+ if (inode)
+ {
+ /* cache the dentry pointer */
+ inode->u.smbfs_i.dentry = dentry;
+ d_instantiate(dentry, inode);
+ error = 0;
+ }
}
+ return error;
+}
- if (dentry->d_name.len > SMB_MAXNAMELEN)
- return -ENAMETOOLONG;
-
- error = smb_proc_create(dentry, &(dentry->d_name), 0, CURRENT_TIME);
- if (error < 0)
- return error;
+/* N.B. Should the mode argument be put into the fattr? */
+static int
+smb_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ int error;
- smb_invalid_dir_cache(dir->i_ino);
+ error = -ENAMETOOLONG;
+ if (dentry->d_name.len > SMB_MAXNAMELEN)
+ goto out;
/* FIXME: In the CIFS create call we get the file in open
* state. Currently we close it directly again, although this
* is not necessary anymore. */
- error = smb_proc_getattr(dentry, &(dentry->d_name), &fattr);
- if (error < 0)
- return error;
-
- fattr.f_ino = smb_invent_inos(1);
-
- inode = smb_iget(dir->i_sb, &fattr);
- if (!inode)
- return -EACCES;
-
- d_instantiate(dentry, inode);
- return 0;
+ error = smb_proc_create(dentry->d_parent, &(dentry->d_name),
+ 0, CURRENT_TIME);
+ if (!error)
+ {
+ error = smb_instantiate(dir, dentry);
+ }
+out:
+ return error;
}
+/* N.B. Should the mode argument be put into the fattr? */
static int
smb_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
- struct smb_fattr fattr;
- struct inode *inode;
int error;
- if (!dir || !S_ISDIR(dir->i_mode))
- {
- printk("smb_mkdir: inode is NULL or not a directory\n");
- return -ENOENT;
- }
-
+ error = -ENAMETOOLONG;
if (dentry->d_name.len > SMB_MAXNAMELEN)
- return -ENAMETOOLONG;
-
- error = smb_proc_mkdir(dentry, &(dentry->d_name));
- if (error)
- return error;
-
- smb_invalid_dir_cache(dir->i_ino);
+ goto out;
- error = smb_proc_getattr(dentry, &(dentry->d_name), &fattr);
- if (error < 0)
- return error;
-
- fattr.f_ino = smb_invent_inos(1);
-
- inode = smb_iget(dir->i_sb, &fattr);
- if (!inode)
- return -EACCES;
-
- d_instantiate(dentry, inode);
- return 0;
+ error = smb_proc_mkdir(dentry->d_parent, &(dentry->d_name));
+ if (!error)
+ {
+ error = smb_instantiate(dir, dentry);
+ }
+out:
+ return error;
}
static int
@@ -363,21 +458,25 @@ smb_rmdir(struct inode *dir, struct dentry *dentry)
{
int error;
- if (!dir || !S_ISDIR(dir->i_mode))
+ error = -ENAMETOOLONG;
+ if (dentry->d_name.len > NFS_MAXNAMLEN)
+ goto out;
+
+ /*
+ * Since the dentry is holding an inode, the file
+ * is in use, so we have to close it first.
+ */
+ if (dentry->d_inode)
+ smb_close(dentry->d_inode);
+ smb_invalid_dir_cache(dir);
+
+ error = smb_proc_rmdir(dentry->d_parent, &(dentry->d_name));
+ if (!error)
{
- printk("smb_rmdir: inode is NULL or not a directory\n");
- return -ENOENT;
+ d_delete(dentry);
}
-
- if (dentry->d_name.len > NFS_MAXNAMLEN)
- return -ENAMETOOLONG;
-
- error = smb_proc_rmdir(dentry, &(dentry->d_name));
- if (error)
- return error;
-
- d_delete(dentry);
- return 0;
+out:
+ return error;
}
static int
@@ -385,64 +484,96 @@ smb_unlink(struct inode *dir, struct dentry *dentry)
{
int error;
- if (!dir || !S_ISDIR(dir->i_mode))
+ error = -ENAMETOOLONG;
+ if (dentry->d_name.len > SMB_MAXNAMELEN)
+ goto out;
+
+ /*
+ * Since the dentry is holding an inode, the file
+ * is in use, so we have to close it first.
+ */
+ if (dentry->d_inode)
+ smb_close(dentry->d_inode);
+ smb_invalid_dir_cache(dir);
+
+ error = smb_proc_unlink(dentry->d_parent, &(dentry->d_name));
+ if (!error)
{
- printk("smb_unlink: inode is NULL or not a directory\n");
- return -ENOENT;
+ d_delete(dentry);
}
-
- if (dentry->d_name.len > SMB_MAXNAMELEN)
- return -ENAMETOOLONG;
-
- error = smb_proc_unlink(dentry, &(dentry->d_name));
- if (error)
- return error;
-
- smb_invalid_dir_cache(dir->i_ino);
-
- d_delete(dentry);
- return 0;
+out:
+ return error;
}
-static int smb_rename(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry)
+static int
+smb_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
{
int error;
+ error = -ENOTDIR;
if (!old_dir || !S_ISDIR(old_dir->i_mode))
{
printk("smb_rename: old inode is NULL or not a directory\n");
- return -ENOENT;
+ goto out;
}
if (!new_dir || !S_ISDIR(new_dir->i_mode))
{
printk("smb_rename: new inode is NULL or not a directory\n");
- return -ENOENT;
+ goto out;
}
+ error = -ENAMETOOLONG;
if (old_dentry->d_name.len > SMB_MAXNAMELEN ||
new_dentry->d_name.len > SMB_MAXNAMELEN)
- return -ENAMETOOLONG;
-
- error = smb_proc_mv(old_dentry, &(old_dentry->d_name),
- new_dentry, &(new_dentry->d_name));
-
+ goto out;
+
+ /*
+ * Since the old and new dentries are holding the files open,
+ * we have to close the files first.
+ */
+ if (old_dentry->d_inode)
+ smb_close(old_dentry->d_inode);
+ if (new_dentry->d_inode)
+ smb_close(new_dentry->d_inode);
+
+ /* Assume success and invalidate now */
+ smb_invalid_dir_cache(old_dir);
+ smb_invalid_dir_cache(new_dir);
+
+ error = smb_proc_mv(old_dentry->d_parent, &(old_dentry->d_name),
+ new_dentry->d_parent, &(new_dentry->d_name));
+ /*
+ * If the new file exists, attempt to delete it.
+ */
if (error == -EEXIST)
{
- error = smb_proc_unlink(old_dentry, &(new_dentry->d_name));
-
+#ifdef SMBFS_PARANOIA
+printk("smb_rename: existing file %s/%s, d_count=%d\n",
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+new_dentry->d_count);
+#endif
+ error = smb_proc_unlink(new_dentry->d_parent,
+ &(new_dentry->d_name));
+#ifdef SMBFS_PARANOIA
+printk("smb_rename: after unlink error=%d\n", error);
+#endif
if (error)
- return error;
+ goto out;
+ d_delete(new_dentry);
- error = smb_proc_mv(old_dentry, &(old_dentry->d_name),
- new_dentry, &(new_dentry->d_name));
+ error = smb_proc_mv(old_dentry->d_parent, &(old_dentry->d_name),
+ new_dentry->d_parent, &(new_dentry->d_name));
}
- if (error)
- return error;
-
- smb_invalid_dir_cache(old_dir->i_ino);
- smb_invalid_dir_cache(new_dir->i_ino);
- return 0;
+ /*
+ * Update the dcache
+ */
+ if (!error)
+ {
+ d_move(old_dentry, new_dentry);
+ }
+out:
+ return error;
}
diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c
index 33fe1778f..9653a7be6 100644
--- a/fs/smbfs/file.c
+++ b/fs/smbfs/file.c
@@ -19,6 +19,10 @@
#include <asm/uaccess.h>
#include <asm/system.h>
+#define SMBFS_PARANOIA 1
+/* #define SMBFS_DEBUG_VERBOSE 1 */
+/* #define pr_debug printk */
+
static inline int
min(int a, int b)
{
@@ -26,8 +30,10 @@ min(int a, int b)
}
static int
-smb_fsync(struct inode *inode, struct file *file)
+smb_fsync(struct file *file, struct dentry * dentry)
{
+ printk("smb_fsync: sync file %s/%s\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
return 0;
}
@@ -39,6 +45,7 @@ smb_readpage_sync(struct inode *inode, struct page *page)
{
unsigned long offset = page->offset;
char *buffer = (char *) page_address(page);
+ struct dentry * dentry = inode->u.smbfs_i.dentry;
int rsize = SMB_SERVER(inode)->opt.max_xmit - (SMB_HEADER_LEN+15);
int result, refresh = 0;
int count = PAGE_SIZE;
@@ -46,14 +53,26 @@ smb_readpage_sync(struct inode *inode, struct page *page)
pr_debug("SMB: smb_readpage_sync(%p)\n", page);
clear_bit(PG_error, &page->flags);
- result = smb_open(inode, O_RDONLY);
+ result = -EIO;
+ if (!dentry) {
+ printk("smb_readpage_sync: no dentry for inode %ld\n",
+ inode->i_ino);
+ goto io_error;
+ }
+
+ result = smb_open(dentry, O_RDONLY);
if (result < 0)
goto io_error;
+ /* Should revalidate inode ... */
do {
if (count < rsize)
rsize = count;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_readpage: reading %s/%s, offset=%ld, buffer=%p, size=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, offset, buffer, rsize);
+#endif
result = smb_proc_read(inode, offset, rsize, buffer);
if (result < 0)
goto io_error;
@@ -81,15 +100,17 @@ io_error:
int
smb_readpage(struct inode *inode, struct page *page)
{
- unsigned long address;
- int error = -1;
+ int error;
pr_debug("SMB: smb_readpage %08lx\n", page_address(page));
+#ifdef SMBFS_PARANOIA
+ if (test_bit(PG_locked, &page->flags))
+ printk("smb_readpage: page already locked!\n");
+#endif
set_bit(PG_locked, &page->flags);
- address = page_address(page);
atomic_inc(&page->count);
error = smb_readpage_sync(inode, page);
- free_page(address);
+ __free_page(page);
return error;
}
@@ -123,6 +144,11 @@ smb_writepage_sync(struct inode *inode, struct page *page,
clear_bit(PG_uptodate, &page->flags);
goto io_error;
}
+ /* N.B. what if result < wsize?? */
+#ifdef SMBFS_PARANOIA
+if (result < wsize)
+printk("smb_writepage_sync: short write, wsize=%d, result=%d\n", wsize, result);
+#endif
refresh = 1;
buffer += wsize;
offset += wsize;
@@ -135,6 +161,7 @@ io_error:
smb_refresh_inode(inode);
clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
return written ? written : result;
}
@@ -145,7 +172,17 @@ io_error:
static int
smb_writepage(struct inode *inode, struct page *page)
{
- return smb_writepage_sync(inode, page, 0, PAGE_SIZE);
+ int result;
+
+#ifdef SMBFS_PARANOIA
+ if (test_bit(PG_locked, &page->flags))
+ printk("smb_writepage: page already locked!\n");
+#endif
+ set_bit(PG_locked, &page->flags);
+ atomic_inc(&page->count);
+ result = smb_writepage_sync(inode, page, 0, PAGE_SIZE);
+ __free_page(page);
+ return result;
}
static int
@@ -153,15 +190,35 @@ smb_updatepage(struct inode *inode, struct page *page, const char *buffer,
unsigned long offset, unsigned int count, int sync)
{
u8 *page_addr;
+ int result;
pr_debug("SMB: smb_updatepage(%x/%ld %d@%ld, sync=%d)\n",
inode->i_dev, inode->i_ino,
count, page->offset+offset, sync);
+#ifdef SMBFS_PARANOIA
+ if (test_bit(PG_locked, &page->flags))
+ printk("smb_updatepage: page already locked!\n");
+#endif
+ set_bit(PG_locked, &page->flags);
+ atomic_inc(&page->count);
+
page_addr = (u8 *) page_address(page);
- copy_from_user(page_addr + offset, buffer, count);
- return smb_writepage_sync(inode, page, offset, count);
+ if (copy_from_user(page_addr + offset, buffer, count))
+ goto bad_fault;
+ result = smb_writepage_sync(inode, page, offset, count);
+out:
+ __free_page(page);
+ return result;
+
+bad_fault:
+ printk("smb_updatepage: fault at page=%p buffer=%p\n", page, buffer);
+ result = -EFAULT;
+ clear_bit(PG_uptodate, &page->flags);
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
+ goto out;
}
static long
@@ -175,23 +232,26 @@ smb_file_read(struct inode * inode, struct file * file,
count, (unsigned long) file->f_pos);
status = smb_revalidate_inode(inode);
- if (status < 0)
- return status;
-
- return generic_file_read(inode, file, buf, count);
+ if (status >= 0)
+ {
+ status = generic_file_read(inode, file, buf, count);
+ }
+ return status;
}
static int
-smb_file_mmap(struct inode * inode, struct file * file,
- struct vm_area_struct * vma)
+smb_file_mmap(struct file * file, struct vm_area_struct * vma)
{
+ struct dentry * dentry = file->f_dentry;
+ struct inode * inode = dentry->d_inode;
int status;
status = smb_revalidate_inode(inode);
- if (status < 0)
- return status;
-
- return generic_file_mmap(inode, file, vma);
+ if (status >= 0)
+ {
+ status = generic_file_mmap(file, vma);
+ }
+ return status;
}
/*
@@ -207,28 +267,34 @@ smb_file_write(struct inode *inode, struct file *file,
inode->i_dev, inode->i_ino, inode->i_count,
count, (unsigned long) file->f_pos);
+ result = -EINVAL;
if (!inode) {
printk("smb_file_write: inode = NULL\n");
- return -EINVAL;
+ goto out;
}
result = smb_revalidate_inode(inode);
if (result < 0)
- return result;
+ goto out;
- result = smb_open(inode, O_WRONLY);
+ result = smb_open(file->f_dentry, O_WRONLY);
if (result < 0)
- return result;
+ goto out;
+ result = -EINVAL;
if (!S_ISREG(inode->i_mode)) {
printk("smb_file_write: write to non-file, mode %07o\n",
inode->i_mode);
- return -EINVAL;
+ goto out;
}
- if (count <= 0)
- return 0;
- return generic_file_write(inode, file, buf, count);
+ result = 0;
+ if (count > 0)
+ {
+ result = generic_file_write(inode, file, buf, count);
+ }
+out:
+ return result;
}
static struct file_operations smb_file_operations =
diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c
index edea04905..9203650e2 100644
--- a/fs/smbfs/inode.c
+++ b/fs/smbfs/inode.c
@@ -26,6 +26,9 @@
#include <asm/system.h>
#include <asm/uaccess.h>
+#define SB_of(server) ((struct super_block *) ((char *)(server) - \
+ (unsigned long)(&((struct super_block *)0)->u.smbfs_sb)))
+
extern int close_fp(struct file *filp);
static void smb_put_inode(struct inode *);
@@ -82,6 +85,23 @@ smb_iget(struct super_block *sb, struct smb_fattr *fattr)
}
static void
+smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr)
+{
+ inode->i_dev = inode->i_sb->s_dev;
+ inode->i_mode = fattr->f_mode;
+ inode->i_nlink = fattr->f_nlink;
+ inode->i_uid = fattr->f_uid;
+ inode->i_gid = fattr->f_gid;
+ inode->i_rdev = fattr->f_rdev;
+ inode->i_size = fattr->f_size;
+ inode->i_mtime = fattr->f_mtime;
+ inode->i_ctime = fattr->f_ctime;
+ inode->i_atime = fattr->f_atime;
+ inode->i_blksize= fattr->f_blksize;
+ inode->i_blocks = fattr->f_blocks;
+}
+
+static void
smb_read_inode(struct inode *inode)
{
pr_debug("smb_iget: %p\n", read_fattr);
@@ -92,18 +112,8 @@ smb_read_inode(struct inode *inode)
printk("smb_read_inode called from invalid point\n");
return;
}
- inode->i_mode = read_fattr->f_mode;
- inode->i_nlink = read_fattr->f_nlink;
- inode->i_uid = read_fattr->f_uid;
- inode->i_gid = read_fattr->f_gid;
- inode->i_rdev = read_fattr->f_rdev;
- inode->i_size = read_fattr->f_size;
- inode->i_mtime = read_fattr->f_mtime;
- inode->i_ctime = read_fattr->f_ctime;
- inode->i_atime = read_fattr->f_atime;
- inode->i_blksize = read_fattr->f_blksize;
- inode->i_blocks = read_fattr->f_blocks;
+ smb_set_inode_attr(inode, read_fattr);
memset(&(inode->u.smbfs_i), 0, sizeof(inode->u.smbfs_i));
if (S_ISREG(inode->i_mode))
@@ -114,39 +124,112 @@ smb_read_inode(struct inode *inode)
inode->i_op = NULL;
}
+/*
+ * This is called if the connection has gone bad ...
+ * try to kill off all the current inodes.
+ */
void
smb_invalidate_inodes(struct smb_sb_info *server)
{
printk("smb_invalidate_inodes\n");
+ shrink_dcache(); /* should shrink only this sb */
+ invalidate_inodes(SB_of(server));
}
int
-smb_revalidate_inode(struct inode *i)
+smb_revalidate_inode(struct inode *ino)
{
+ struct dentry * dentry = ino->u.smbfs_i.dentry;
+ int error = 0;
+
pr_debug("smb_revalidate_inode\n");
- return 0;
+ if (!ino)
+ goto bad_no_inode;
+ dentry = ino->u.smbfs_i.dentry;
+#if 0
+ if (dentry)
+ {
+ printk("smb_revalidate: checking %s/%s\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ }
+#endif
+out:
+ return error;
+
+bad_no_inode:
+ printk("smb_revalidate: no inode!\n");
+ error = -EINVAL;
+ goto out;
}
int
-smb_refresh_inode(struct inode *i)
+smb_refresh_inode(struct inode *ino)
{
+ struct dentry * dentry = ino->u.smbfs_i.dentry;
+ struct smb_fattr fattr;
+ int error;
+
pr_debug("smb_refresh_inode\n");
- return 0;
+ error = -EIO;
+ if (!dentry) {
+ printk("smb_refresh_inode: no dentry, can't refresh\n");
+ goto out;
+ }
+
+ /* N.B. Should check for changes of important fields! cf. NFS */
+ error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &fattr);
+ if (!error)
+ {
+ smb_set_inode_attr(ino, &fattr);
+ }
+out:
+ return error;
}
+/*
+ * This routine is called for every iput().
+ */
static void
smb_put_inode(struct inode *ino)
{
+ struct dentry * dentry;
pr_debug("smb_put_inode: count = %d\n", ino->i_count);
- if (smb_close(ino))
- printk("smbfs: could not close inode\n");
+ if (ino->i_count > 1) {
+ /*
+ * Check whether the dentry still holds this inode.
+ * This looks scary, but should work ... d_inode is
+ * cleared before iput() in the dcache.
+ */
+ dentry = (struct dentry *) ino->u.smbfs_i.dentry;
+ if (dentry && dentry->d_inode != ino) {
+ ino->u.smbfs_i.dentry = NULL;
+#if 1
+printk("smb_put_inode: cleared dentry for %s/%s (%ld), count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ino->i_ino, ino->i_count);
+#endif
+ }
+ } else {
+ /*
+ * Last use ... clear i_nlink to force
+ * smb_delete_inode to be called.
+ */
+ ino->i_nlink = 0;
+ }
}
+/*
+ * This routine is called when i_nlink == 0 and i_count goes to 0.
+ * All blocking cleanup operations need to go here to avoid races.
+ */
static void
-smb_delete_inode(struct inode *i)
+smb_delete_inode(struct inode *ino)
{
pr_debug("smb_delete_inode\n");
+ if (smb_close(ino))
+ printk("smb_delete_inode: could not close inode %ld\n",
+ ino->i_ino);
+ clear_inode(ino);
}
static void
@@ -181,33 +264,20 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
struct smb_fattr root;
kdev_t dev = sb->s_dev;
unsigned char *packet;
+ struct inode *root_inode;
+ struct dentry *dentry;
MOD_INC_USE_COUNT;
- if (!data) {
- printk("smb_read_super: missing data argument\n");
- sb->s_dev = 0;
- MOD_DEC_USE_COUNT;
- return NULL;
- }
+ if (!data)
+ goto out_no_data;
if (data->version != SMB_MOUNT_VERSION)
- {
- printk(KERN_ERR "smb_read_super: wrong data argument."
- " Recompile smbmount.\n");
- sb->s_dev = 0;
- MOD_DEC_USE_COUNT;
- return NULL;
- }
+ goto out_wrong_data;
packet = smb_vmalloc(SMB_INITIAL_PACKET_SIZE);
if (!packet)
- {
- pr_debug("smb_read_super: could not alloc packet\n");
- sb->s_dev = 0;
- MOD_DEC_USE_COUNT;
- return NULL;
- }
+ goto out_no_mem;
lock_super(sb);
@@ -222,7 +292,7 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
sb->u.smbfs_sb.conn_pid = 0;
sb->u.smbfs_sb.packet = packet;
sb->u.smbfs_sb.packet_size = SMB_INITIAL_PACKET_SIZE;
- sb->u.smbfs_sb.generation = 1;
+ sb->u.smbfs_sb.generation = 0;
sb->u.smbfs_sb.m = *data;
sb->u.smbfs_sb.m.file_mode = (sb->u.smbfs_sb.m.file_mode &
@@ -232,18 +302,38 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
smb_init_root_dirent(&(sb->u.smbfs_sb), &root);
+ sb->s_root = NULL;
unlock_super(sb);
- sb->s_root = d_alloc_root(smb_iget(sb, &root), NULL);
- if (!sb->s_root)
- {
- sb->s_dev = 0;
- printk(KERN_ERR "smb_read_super: get root inode failed\n");
- smb_vfree(sb->u.smbfs_sb.packet);
- MOD_DEC_USE_COUNT;
- return NULL;
- }
+ root_inode = smb_iget(sb, &root);
+ if (!root_inode)
+ goto out_no_root;
+ dentry = d_alloc_root(root_inode, NULL);
+ if (!dentry)
+ goto out_no_root;
+ root_inode->u.smbfs_i.dentry = dentry;
+ sb->s_root = dentry;
return sb;
+
+out_no_data:
+ printk("smb_read_super: missing data argument\n");
+ goto out;
+out_wrong_data:
+ printk(KERN_ERR "smb_read_super: wrong data argument."
+ " Recompile smbmount.\n");
+ goto out;
+out_no_mem:
+ pr_debug("smb_read_super: could not alloc packet\n");
+ goto out;
+out_no_root:
+ sb->s_dev = 0; /* iput() might block */
+ printk(KERN_ERR "smb_read_super: get root inode failed\n");
+ iput(root_inode);
+ smb_vfree(packet);
+out:
+ sb->s_dev = 0;
+ MOD_DEC_USE_COUNT;
+ return NULL;
}
static int
@@ -265,32 +355,46 @@ smb_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
int
smb_notify_change(struct inode *inode, struct iattr *attr)
{
- int error = 0;
+ struct dentry *dentry = inode->u.smbfs_i.dentry;
+ int error;
+
+ error = -EIO;
+ if (!dentry)
+ {
+ printk("smb_notify_change: no dentry for inode!\n");
+ goto out;
+ }
if ((error = inode_change_ok(inode, attr)) < 0)
- return error;
+ goto out;
+ error = -EPERM;
if (((attr->ia_valid & ATTR_UID) &&
(attr->ia_uid != SMB_SERVER(inode)->m.uid)))
- return -EPERM;
+ goto out;
if (((attr->ia_valid & ATTR_GID) &&
(attr->ia_uid != SMB_SERVER(inode)->m.gid)))
- return -EPERM;
+ goto out;
if (((attr->ia_valid & ATTR_MODE) &&
(attr->ia_mode & ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO))))
- return -EPERM;
+ goto out;
+
+ /*
+ * Assume success and invalidate the parent's dir cache
+ */
+ smb_invalid_dir_cache(dentry->d_parent->d_inode);
if ((attr->ia_valid & ATTR_SIZE) != 0)
{
- if ((error = smb_open(inode, O_WRONLY)) < 0)
- goto fail;
+ if ((error = smb_open(dentry, O_WRONLY)) < 0)
+ goto out;
if ((error = smb_proc_trunc(SMB_SERVER(inode),
inode->u.smbfs_i.fileid,
attr->ia_size)) < 0)
- goto fail;
+ goto out;
}
if ((attr->ia_valid & (ATTR_CTIME | ATTR_MTIME | ATTR_ATIME)) != 0)
{
@@ -300,37 +404,31 @@ smb_notify_change(struct inode *inode, struct iattr *attr)
fattr.attr = 0;
fattr.f_size = inode->i_size;
fattr.f_blksize = inode->i_blksize;
+ fattr.f_ctime = inode->i_ctime;
+ fattr.f_mtime = inode->i_mtime;
+ fattr.f_atime = inode->i_atime;
if ((attr->ia_valid & ATTR_CTIME) != 0)
fattr.f_ctime = attr->ia_ctime;
- else
- fattr.f_ctime = inode->i_ctime;
if ((attr->ia_valid & ATTR_MTIME) != 0)
fattr.f_mtime = attr->ia_mtime;
- else
- fattr.f_mtime = inode->i_mtime;
if ((attr->ia_valid & ATTR_ATIME) != 0)
fattr.f_atime = attr->ia_atime;
- else
- fattr.f_atime = inode->i_atime;
- if ((error = smb_proc_setattr(SMB_SERVER(inode),
- inode, &fattr)) >= 0)
+ error = smb_proc_setattr(SMB_SERVER(inode), dentry, &fattr);
+ if (error >= 0)
{
inode->i_ctime = fattr.f_ctime;
inode->i_mtime = fattr.f_mtime;
inode->i_atime = fattr.f_atime;
}
}
- fail:
-/* smb_invalid_dir_cache(smb_info_ino(SMB_INOP(inode)->dir));*/
-
+out:
return error;
}
-
#ifdef DEBUG_SMB_MALLOC
int smb_malloced;
int smb_current_kmalloced;
diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c
index 311457a74..57b4be6fb 100644
--- a/fs/smbfs/proc.c
+++ b/fs/smbfs/proc.c
@@ -29,7 +29,12 @@
#define SMB_DIRINFO_SIZE 43
#define SMB_STATUS_SIZE 21
+#define SMBFS_PARANOIA 1
+/* #define SMBFS_DEBUG_VERBOSE 1 */
+/* #define pr_debug printk */
+
static int smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc);
+void smb_close_socket(struct smb_sb_info *);
static inline int
min(int a, int b)
@@ -79,6 +84,15 @@ smb_encode_smb_length(__u8 * p, __u32 len)
return p + 4;
}
+/*
+ * Return the server for the specified dentry
+ * N.B. Make this a #define in the smb header
+ */
+static struct smb_sb_info * server_from_dentry(struct dentry * dentry)
+{
+ return &dentry->d_sb->u.smbfs_sb;
+}
+
static int smb_d_path(struct dentry * entry, char * buf)
{
if (IS_ROOT(entry)) {
@@ -283,11 +297,15 @@ smb_errno(int errcls, int error)
return EEXIST;
case 87:
return 0; /* Unknown error!! */
+ case 123: /* Invalid name?? e.g. .tmp* */
+ return ENOENT;
/* This next error seems to occur on an mv when
* the destination exists */
case 183:
return EEXIST;
default:
+ printk("smb_errno: ERRDOS code %d, returning EIO\n",
+ error);
return EIO;
} else if (errcls == ERRSRV)
switch (error)
@@ -301,6 +319,8 @@ smb_errno(int errcls, int error)
case ERRaccess:
return EACCES;
default:
+ printk("smb_errno: ERRSRV code %d, returning EIO\n",
+ error);
return EIO;
} else if (errcls == ERRHRD)
switch (error)
@@ -322,9 +342,14 @@ smb_errno(int errcls, int error)
case ERRlock:
return EDEADLK;
default:
+ printk("smb_errno: ERRHRD code %d, returning EIO\n",
+ error);
return EIO;
} else if (errcls == ERRCMD)
+ {
+ printk("smb_errno: ERRCMD code %d, returning EIO\n", error);
return EIO;
+ }
return 0;
}
@@ -349,6 +374,7 @@ static int
smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc)
{
int result = 0;
+
s->rcls = 0;
s->err = 0;
@@ -371,86 +397,133 @@ smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc)
return result;
}
-/* smb_retry: This function should be called when smb_request_ok has
+/*
+ * smb_retry: This function should be called when smb_request_ok has
indicated an error. If the error was indicated because the
connection was killed, we try to reconnect. If smb_retry returns 0,
the error was indicated for another reason, so a retry would not be
- of any use. */
+ of any use.
+ * N.B. The server must be locked for this call.
+ */
static int
smb_retry(struct smb_sb_info *server)
{
+ int result = 0;
+
if (server->state != CONN_INVALID)
- {
- return 0;
- }
- if (server->sock_file != NULL)
- {
- close_fp(server->sock_file);
- server->sock_file = NULL;
- }
+ goto out;
+
+ smb_close_socket(server);
if (server->conn_pid == 0)
{
+ printk("smb_retry: no connection process\n");
server->state = CONN_RETRIED;
- return 0;
+ goto out;
}
+ printk("smb_retry: signalling process %d\n", server->conn_pid);
kill_proc(server->conn_pid, SIGUSR1, 0);
server->conn_pid = 0;
+ /*
+ * Block here until we get a new connection.
+ * N.B. This needs to be fixed ... we should wait in an
+ * interruptible sleep for CONN_VALID.
+ */
+ printk("smb_retry: blocking for new connection\n");
smb_lock_server(server);
- if (server->sock_file != NULL)
+ if (server->state == CONN_VALID)
{
- server->state = CONN_VALID;
- return 1;
+ printk("smb_retry: new connection pid=%d\n", server->conn_pid);
+ result = 1;
}
- return 0;
+out:
+ return result;
}
+/*
+ * This is called with the server locked after a successful smb_newconn().
+ * It installs the new connection pid, sets server->state to CONN_VALID,
+ * and unlocks the server.
+ * N.B. The first call is made without locking the server -- need to fix!
+ */
int
smb_offerconn(struct smb_sb_info *server)
{
+ int error;
+
+ error = -EACCES;
if (!suser() && (current->uid != server->m.mounted_uid))
- {
- return -EACCES;
- }
+ goto out;
+
server->conn_pid = current->pid;
- return 0;
+ server->state = CONN_VALID;
+ printk("smb_offerconn: state valid, pid=%d\n", server->conn_pid);
+ error = 0;
+
+ /*
+ * The initial call may be made without the server locked?
+ */
+out:
+ if (atomic_read(&server->sem.count) != 1)
+ smb_unlock_server(server);
+ else
+ printk("smb_offerconn: server not locked, count=%d\n",
+ atomic_read(&server->sem.count));
+ return error;
}
+/*
+ * This must be called with the server locked.
+ * N.B. The first call is made without locking the server -- need to fix!
+ */
int
smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
{
struct file *filp;
+ int error;
+ error = -EBADF;
if (opt->fd >= NR_OPEN || !(filp = current->files->fd[opt->fd]))
- {
- return -EBADF;
- }
+ goto out_unlock;
if (!S_ISSOCK(filp->f_dentry->d_inode->i_mode))
- {
- return -EBADF;
- }
+ goto out_unlock;
+ if (!S_ISSOCK(filp->f_dentry->d_inode->i_mode))
+ goto out_unlock;
+
+ error = -EACCES;
if (!suser() && (current->uid != server->m.mounted_uid))
- {
- return -EACCES;
- }
- if (server->sock_file != NULL)
- {
- close_fp(server->sock_file);
- server->sock_file = NULL;
- }
+ goto out_unlock;
+
+ /*
+ * Make sure the old socket is closed
+ */
+ smb_close_socket(server);
+
filp->f_count += 1;
server->sock_file = filp;
smb_catch_keepalive(server);
server->opt = *opt;
pr_debug("smb_newconn: protocol = %d\n", server->opt.protocol);
- server->conn_pid = 0;
server->generation += 1;
- smb_unlock_server(server);
- return 0;
+ error = 0;
+
+out:
+ return error;
+
+ /*
+ * Unlock now if an error occurred.
+ */
+out_unlock:
+ if (atomic_read(&server->sem.count) != 1)
+ smb_unlock_server(server);
+ else
+ printk("smb_newconn: server not locked, count=%d\n",
+ atomic_read(&server->sem.count));
+ goto out;
}
/* smb_setup_header: We completely set up the packet. You only have to
@@ -503,17 +576,15 @@ smb_setup_bcc(struct smb_sb_info *server, __u8 * p)
SMB_HEADER_LEN + 2 * SMB_WCT(packet) - 2 + bcc);
}
-
/*
* We're called with the server locked, and we leave it that way. We
* try maximum permissions.
*/
static int
-smb_proc_open(struct dentry *dir)
+smb_proc_open(struct smb_sb_info *server, struct dentry *dir)
{
struct inode *ino = dir->d_inode;
- struct smb_sb_info *server = SMB_SERVER(ino);
int error;
char *p;
@@ -532,7 +603,7 @@ smb_proc_open(struct dentry *dir)
if ((error != -EACCES) && (error != -ETXTBSY)
&& (error != -EROFS))
- return error;
+ goto out;
p = smb_setup_header(server, SMBopen, 2, 0);
WSET(server->packet, smb_vwv0, 0x40); /* read only */
@@ -545,40 +616,56 @@ smb_proc_open(struct dentry *dir)
{
if (smb_retry(server))
goto retry;
-
- return error;
+ goto out;
}
}
/* We should now have data in vwv[0..6]. */
ino->u.smbfs_i.fileid = WVAL(server->packet, smb_vwv0);
- ino->u.smbfs_i.attr = WVAL(server->packet, smb_vwv1);
+ ino->u.smbfs_i.attr = WVAL(server->packet, smb_vwv1);
ino->u.smbfs_i.access = WVAL(server->packet, smb_vwv6);
ino->u.smbfs_i.access &= 3;
ino->u.smbfs_i.open = server->generation;
pr_debug("smb_proc_open: entry->access = %d\n", ino->u.smbfs_i.access);
- return 0;
+out:
+ return error;
}
int
smb_open(struct dentry *dir, int wish)
{
- struct inode *i=dir->d_inode;
- struct smb_sb_info *server = SMB_SERVER(i);
- int result = -EACCES;
+ struct inode *i = dir->d_inode;
+ int result;
- smb_lock_server(server);
+ result = -EIO;
+ if (!i)
+ {
+ printk("smb_open: no inode for dentry %s/%s\n",
+ dir->d_parent->d_name.name, dir->d_name.name);
+ goto out;
+ }
- if (!smb_is_open(i)) {
- int error = smb_proc_open(dir);
- if (error) {
- smb_unlock_server(server);
- return error;
+ /*
+ * If the inode is already open, we don't need to lock the server.
+ */
+ if (!smb_is_open(i))
+ {
+ struct smb_sb_info *server = SMB_SERVER(i);
+ smb_lock_server(server);
+ result = smb_proc_open(server, dir);
+ smb_unlock_server(server);
+ if (result)
+ {
+ printk("smb_open: %s/%s open failed, result=%d\n",
+ dir->d_parent->d_name.name, dir->d_name.name,
+ result);
+ goto out;
}
}
+ result = -EACCES;
if (((wish == O_RDONLY) && ((i->u.smbfs_i.access == O_RDONLY)
|| (i->u.smbfs_i.access == O_RDWR)))
|| ((wish == O_WRONLY) && ((i->u.smbfs_i.access == O_WRONLY)
@@ -586,7 +673,7 @@ smb_open(struct dentry *dir, int wish)
|| ((wish == O_RDWR) && (i->u.smbfs_i.access == O_RDWR)))
result = 0;
- smb_unlock_server(server);
+out:
return result;
}
@@ -600,24 +687,21 @@ static int smb_proc_close(struct smb_sb_info *server,
DSET(server->packet, smb_vwv1, mtime);
return smb_request_ok(server, SMBclose, 0, 0);
}
-
-int smb_close(struct dentry *dir)
+int
+smb_close(struct inode *ino)
{
- struct inode *ino = dir->d_inode;
- struct smb_sb_info *server = SMB_SERVER(ino);
- int result;
-
- smb_lock_server(server);
+ int result = 0;
- if (!smb_is_open(ino)) {
+ if (smb_is_open(ino))
+ {
+ struct smb_sb_info *server = SMB_SERVER(ino);
+ smb_lock_server(server);
+ result = smb_proc_close(server, ino->u.smbfs_i.fileid,
+ ino->i_mtime);
smb_unlock_server(server);
- return 0;
+ ino->u.smbfs_i.open = 0;
}
-
- result = smb_proc_close(server, ino->u.smbfs_i.fileid, ino->i_mtime);
- ino->u.smbfs_i.open = 0;
- smb_unlock_server(server);
return result;
}
@@ -633,38 +717,49 @@ smb_proc_read(struct inode *ino, off_t offset, long count, char *data)
struct smb_sb_info *server = SMB_SERVER(ino);
__u16 returned_count, data_len;
char *buf;
- int error;
+ int result;
+ struct dentry * dentry;
+
+ if (!ino || !(dentry = ino->u.smbfs_i.dentry))
+ {
+ printk("smb_proc_read: no inode!\n");
+ return -EIO;
+ }
smb_lock_server(server);
smb_setup_header(server, SMBread, 5, 0);
- buf = server->packet;
+ /* Achtung! Do not refer to the cached packet after the request! */
+ buf = server->packet;
WSET(buf, smb_vwv0, ino->u.smbfs_i.fileid);
WSET(buf, smb_vwv1, count);
DSET(buf, smb_vwv2, offset);
WSET(buf, smb_vwv4, 0);
- if ((error = smb_request_ok(server, SMBread, 5, -1)) < 0)
- {
- smb_unlock_server(server);
- return error;
- }
- returned_count = WVAL(buf, smb_vwv0);
+ result = smb_request_ok(server, SMBread, 5, -1);
+ if (result < 0)
+ goto out;
+#if 0
+printk("smb_proc_read: file %s/%s, result=%d, packet=%p\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, result, server->packet);
+#endif
+ returned_count = WVAL(server->packet, smb_vwv0);
buf = SMB_BUF(server->packet);
data_len = WVAL(buf, 1);
-
memcpy(data, buf+3, data_len);
- smb_unlock_server(server);
-
if (returned_count != data_len)
{
printk(KERN_NOTICE "smb_proc_read: returned != data_len\n");
printk(KERN_NOTICE "smb_proc_read: ret_c=%d, data_len=%d\n",
returned_count, data_len);
}
- return data_len;
+ result = data_len;
+
+out:
+ smb_unlock_server(server);
+ return result;
}
int
@@ -697,18 +792,17 @@ int
smb_proc_create(struct dentry *dir, struct qstr *name,
__u16 attr, time_t ctime)
{
+ struct smb_sb_info *server;
int error;
char *p;
- struct inode *i=dir->d_inode;
- struct smb_sb_info *server = SMB_SERVER(i);
- char *buf;
+ server = server_from_dentry(dir);
smb_lock_server(server);
+
retry:
- buf = server->packet;
p = smb_setup_header(server, SMBcreate, 3, 0);
- WSET(buf, smb_vwv0, attr);
- DSET(buf, smb_vwv1, utc2local(ctime));
+ WSET(server->packet, smb_vwv0, attr);
+ DSET(server->packet, smb_vwv1, utc2local(ctime));
*p++ = 4;
p = smb_encode_path(server, p, dir, name);
smb_setup_bcc(server, p);
@@ -719,23 +813,25 @@ smb_proc_create(struct dentry *dir, struct qstr *name,
{
goto retry;
}
- smb_unlock_server(server);
- return error;
+ goto out;
}
- smb_proc_close(server, WVAL(buf, smb_vwv0), CURRENT_TIME);
- smb_unlock_server(server);
+ smb_proc_close(server, WVAL(server->packet, smb_vwv0), CURRENT_TIME);
+ error = 0;
- return 0;
+out:
+ smb_unlock_server(server);
+ return error;
}
int
smb_proc_mv(struct dentry *odir, struct qstr *oname,
struct dentry *ndir, struct qstr *nname)
{
+ struct smb_sb_info *server;
char *p;
- struct smb_sb_info *server = SMB_SERVER(odir->d_inode);
int result;
+ server = server_from_dentry(odir);
smb_lock_server(server);
retry:
@@ -761,10 +857,11 @@ smb_proc_mv(struct dentry *odir, struct qstr *oname,
int
smb_proc_mkdir(struct dentry *dir, struct qstr *name)
{
+ struct smb_sb_info *server;
char *p;
int result;
- struct smb_sb_info *server = SMB_SERVER(dir->d_inode);
+ server = server_from_dentry(dir);
smb_lock_server(server);
retry:
@@ -787,10 +884,11 @@ smb_proc_mkdir(struct dentry *dir, struct qstr *name)
int
smb_proc_rmdir(struct dentry *dir, struct qstr *name)
{
+ struct smb_sb_info *server;
char *p;
int result;
- struct smb_sb_info *server = SMB_SERVER(dir->d_inode);
+ server = server_from_dentry(dir);
smb_lock_server(server);
retry:
@@ -813,10 +911,11 @@ smb_proc_rmdir(struct dentry *dir, struct qstr *name)
int
smb_proc_unlink(struct dentry *dir, struct qstr *name)
{
+ struct smb_sb_info *server;
char *p;
- struct smb_sb_info *server = SMB_SERVER(dir->d_inode);
int result;
+ server = server_from_dentry(dir);
smb_lock_server(server);
retry:
@@ -882,22 +981,18 @@ smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
static void
smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
{
+ fattr->f_mode = server->m.file_mode;
if (fattr->attr & aDIR)
{
fattr->f_mode = server->m.dir_mode;
fattr->f_size = 512;
- } else
- {
- fattr->f_mode = server->m.file_mode;
}
+ fattr->f_blocks = 0; /* already set to zero? */
if ((fattr->f_blksize != 0) && (fattr->f_size != 0))
{
fattr->f_blocks =
(fattr->f_size - 1) / fattr->f_blksize + 1;
- } else
- {
- fattr->f_blocks = 0;
}
return;
}
@@ -982,13 +1077,13 @@ smb_proc_readdir_short(struct smb_sb_info *server, struct dentry *dir, int fpos,
smb_lock_server(server);
retry:
- buf = server->packet;
first = 1;
total_count = 0;
current_entry = entry;
while (1)
{
+ buf = server->packet;
if (first == 1)
{
p = smb_setup_header(server, SMBsearch, 2, 0);
@@ -1098,6 +1193,7 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p,
struct smb_dirent *entry, int level)
{
char *result;
+ unsigned int len;
smb_init_dirent(server, &(entry->attr));
@@ -1105,20 +1201,24 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p,
{
/* We might add more levels later... */
case 1:
- entry->len = *(p+26);
- strncpy(entry->name, p + 27, entry->len);
- entry->name[entry->len] = '\0';
- entry->attr.f_size = DVAL(p, 16);
- entry->attr.attr = *(p+24);
-
entry->attr.f_ctime = date_dos2unix(WVAL(p, 6), WVAL(p, 4));
entry->attr.f_atime = date_dos2unix(WVAL(p, 10), WVAL(p, 8));
entry->attr.f_mtime = date_dos2unix(WVAL(p, 14), WVAL(p, 12));
- result = p + 28 + *(p+26);
+ entry->attr.f_size = DVAL(p, 16);
+ entry->attr.attr = *(p+24);
+ /*
+ * Achtung, lengths can go up to 255
+ */
+ len = *((unsigned char *) p + 26);
+ entry->len = len;
+ strncpy(entry->name, p + 27, len);
+ entry->name[len] = '\0';
+
+ result = p + 28 + len;
break;
default:
- pr_debug("Unknown long filename format %d\n", level);
+ printk("smb_decode: Unknown long filename format %d\n", level);
result = p + WVAL(p, 0);
}
@@ -1135,6 +1235,7 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p,
}
smb_finish_dirent(server, &(entry->attr));
+
return result;
}
@@ -1148,7 +1249,7 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
char *p;
char *lastname;
- int lastname_len;
+ unsigned lastname_len;
int i;
int first, entries, entries_seen;
@@ -1168,7 +1269,7 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
int ff_dir_handle = 0;
int loop_count = 0;
- char param[SMB_MAXPATHLEN + 2 + 12];
+ char param[SMB_MAXPATHLEN + 2 + 12]; /* too long for the stack! */
int mask_len;
char *mask = &(param[12]);
@@ -1197,6 +1298,7 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
{
printk(KERN_WARNING "smb_proc_readdir_long: "
"Looping in FIND_NEXT??\n");
+ entries = -EIO;
break;
}
if (first != 0)
@@ -1243,15 +1345,23 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
{
goto retry;
}
- pr_debug("smb_proc_readdir_long: "
- "got error from trans2_request\n");
+#ifdef SMBFS_PARANOIA
+printk("smb_proc_readdir_long: trans2_request error=%d\n", result);
+#endif
+ entries = result;
break;
}
if (server->rcls != 0)
- {
- result = -EIO;
+ {
+#ifdef SMBFS_PARANOIA
+printk("smb_proc_readdir_long: error, rcls=%d, err=%d\n",
+server->rcls, server->err);
+#endif
+ /* Why isn't this considered an error? */
+ /* entries = -EIO; */
break;
}
+
/* parse out some important return info */
if (first != 0)
{
@@ -1278,17 +1388,15 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
lastname_len = 0;
if (ff_lastname > 0)
{
+ ff_resume_key = 0;
+ lastname = p + ff_lastname;
switch (info_level)
{
case 260:
- lastname = p + ff_lastname;
lastname_len = resp_data_len - ff_lastname;
- ff_resume_key = 0;
break;
case 1:
- lastname = p + ff_lastname + 1;
- lastname_len = *(p+ff_lastname);
- ff_resume_key = 0;
+ lastname_len = *((unsigned char *) lastname++);
break;
}
}
@@ -1320,11 +1428,12 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
entry->f_pos = entries_seen;
entries += 1;
}
- if (entries >= cache_size)
- {
- goto finished;
- }
entries_seen += 1;
+ if (entries < cache_size)
+ continue;
+
+ /* cache is full */
+ goto finished;
}
pr_debug("received %d entries (eos=%d resume=%d)\n",
@@ -1342,8 +1451,9 @@ int
smb_proc_readdir(struct dentry *dir, int fpos,
int cache_size, struct smb_dirent *entry)
{
- struct smb_sb_info *server = SMB_SERVER(dir->d_inode);
+ struct smb_sb_info *server;
+ server = server_from_dentry(dir);
if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2)
return smb_proc_readdir_long(server, dir, fpos, cache_size,
entry);
@@ -1353,18 +1463,15 @@ smb_proc_readdir(struct dentry *dir, int fpos,
}
static int
-smb_proc_getattr_core(struct dentry *dir, struct qstr *name,
- struct smb_fattr *attr)
+smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir,
+ struct qstr *name, struct smb_fattr *attr)
{
int result;
char *p;
- struct smb_sb_info *server = SMB_SERVER(dir->d_inode);
- char *buf;
smb_lock_server(server);
retry:
- buf = server->packet;
p = smb_setup_header(server, SMBgetatr, 0, 0);
*p++ = 4;
p = smb_encode_path(server, p, dir, name);
@@ -1373,26 +1480,24 @@ smb_proc_getattr_core(struct dentry *dir, struct qstr *name,
if ((result = smb_request_ok(server, SMBgetatr, 10, 0)) < 0)
{
if (smb_retry(server))
- {
goto retry;
- }
- smb_unlock_server(server);
- return result;
+ goto out;
}
- attr->attr = WVAL(buf, smb_vwv0);
- attr->f_ctime = attr->f_atime =
- attr->f_mtime = local2utc(DVAL(buf, smb_vwv1));
+ attr->attr = WVAL(server->packet, smb_vwv0);
+ attr->f_ctime = attr->f_atime = attr->f_mtime =
+ local2utc(DVAL(server->packet, smb_vwv1));
+ attr->f_size = DVAL(server->packet, smb_vwv3);
+ result = 0;
- attr->f_size = DVAL(buf, smb_vwv3);
+out:
smb_unlock_server(server);
- return 0;
+ return result;
}
static int
-smb_proc_getattr_trans2(struct dentry *dir, struct qstr *name,
- struct smb_fattr *attr)
+smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir,
+ struct qstr *name, struct smb_fattr *attr)
{
- struct smb_sb_info *server = SMB_SERVER(dir->d_inode);
char param[SMB_MAXPATHLEN + 20];
char *p;
int result;
@@ -1412,26 +1517,22 @@ smb_proc_getattr_trans2(struct dentry *dir, struct qstr *name,
0, NULL, p - param, param,
&resp_data_len, &resp_data,
&resp_param_len, &resp_param);
-
+
if (server->rcls != 0)
{
- smb_unlock_server(server);
- return -smb_errno(server->rcls, server->err);
+ result = -smb_errno(server->rcls, server->err);
+ goto out;
}
if (result < 0)
{
if (smb_retry(server))
- {
goto retry;
- }
- smb_unlock_server(server);
- return result;
+ goto out;
}
+ result = -ENOENT;
if (resp_data_len < 22)
- {
- smb_unlock_server(server);
- return -ENOENT;
- }
+ goto out;
+
attr->f_ctime = date_dos2unix(WVAL(resp_data, 2),
WVAL(resp_data, 0));
attr->f_atime = date_dos2unix(WVAL(resp_data, 6),
@@ -1440,24 +1541,28 @@ smb_proc_getattr_trans2(struct dentry *dir, struct qstr *name,
WVAL(resp_data, 8));
attr->f_size = DVAL(resp_data, 12);
attr->attr = WVAL(resp_data, 20);
- smb_unlock_server(server);
+ result = 0;
- return 0;
+out:
+ smb_unlock_server(server);
+ return result;
}
-int smb_proc_getattr(struct dentry *dir, struct qstr *name,
+int
+smb_proc_getattr(struct dentry *dir, struct qstr *name,
struct smb_fattr *fattr)
{
- struct smb_sb_info *server = SMB_SERVER(dir->d_inode);
- int result = 0;
+ struct smb_sb_info *server;
+ int result = -1;
+ server = server_from_dentry(dir);
smb_init_dirent(server, fattr);
if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2)
- result = smb_proc_getattr_trans2(dir, name, fattr);
+ result = smb_proc_getattr_trans2(server, dir, name, fattr);
- if ((server->opt.protocol < SMB_PROTOCOL_LANMAN2) || (result < 0))
- result = smb_proc_getattr_core(dir, name, fattr);
+ if (result < 0)
+ result = smb_proc_getattr_core(server, dir, name, fattr);
smb_finish_dirent(server, fattr);
@@ -1546,12 +1651,12 @@ int
smb_proc_setattr(struct smb_sb_info *server, struct dentry *dir,
struct smb_fattr *fattr)
{
- int result;
+ int result = -1;
if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2)
result = smb_proc_setattr_trans2(server, dir, fattr);
- if ((server->opt.protocol < SMB_PROTOCOL_LANMAN2) || (result < 0))
+ if (result < 0)
result = smb_proc_setattr_core(server, dir, fattr);
return result;
@@ -1560,9 +1665,9 @@ smb_proc_setattr(struct smb_sb_info *server, struct dentry *dir,
int
smb_proc_dskattr(struct super_block *sb, struct statfs *attr)
{
+ struct smb_sb_info *server = &(sb->u.smbfs_sb);
int error;
char *p;
- struct smb_sb_info *server = &(sb->u.smbfs_sb);
smb_lock_server(server);
@@ -1573,16 +1678,17 @@ smb_proc_dskattr(struct super_block *sb, struct statfs *attr)
{
if (smb_retry(server))
goto retry;
-
- smb_unlock_server(server);
- return error;
+ goto out;
}
p = SMB_VWV(server->packet);
attr->f_bsize = WVAL(p, 2) * WVAL(p, 4);
attr->f_blocks = WVAL(p, 0);
attr->f_bavail = attr->f_bfree = WVAL(p, 6);
+ error = 0;
+
+out:
smb_unlock_server(server);
- return 0;
+ return error;
}
int
diff --git a/fs/smbfs/sock.c b/fs/smbfs/sock.c
index 1687be10b..aceca122b 100644
--- a/fs/smbfs/sock.c
+++ b/fs/smbfs/sock.c
@@ -24,10 +24,13 @@
#include <asm/uaccess.h>
+#define SMBFS_PARANOIA 1
+/* #define SMBFS_DEBUG_VERBOSE 1 */
+
#define _S(nr) (1<<((nr)-1))
static int
-_recvfrom(struct socket *sock, unsigned char *ubuf, int size,
+_recvfrom(struct socket *socket, unsigned char *ubuf, int size,
unsigned flags)
{
struct iovec iov;
@@ -43,14 +46,14 @@ _recvfrom(struct socket *sock, unsigned char *ubuf, int size,
iov.iov_len = size;
memset(&scm, 0,sizeof(scm));
- size=sock->ops->recvmsg(sock, &msg, size, flags, &scm);
+ size=socket->ops->recvmsg(socket, &msg, size, flags, &scm);
if(size>=0)
- scm_recv(sock,&msg,&scm,flags);
+ scm_recv(socket,&msg,&scm,flags);
return size;
}
static int
-_send(struct socket *sock, const void *buff, int len)
+_send(struct socket *socket, const void *buff, int len)
{
struct iovec iov;
struct msghdr msg;
@@ -69,163 +72,199 @@ _send(struct socket *sock, const void *buff, int len)
msg.msg_flags = 0;
- err = scm_send(sock, &msg, &scm);
- if (err < 0)
- return err;
- err = sock->ops->sendmsg(sock, &msg, len, &scm);
- scm_destroy(&scm);
+ err = scm_send(socket, &msg, &scm);
+ if (err >= 0)
+ {
+ err = socket->ops->sendmsg(socket, &msg, len, &scm);
+ scm_destroy(&scm);
+ }
return err;
}
+/*
+ * N.B. What happens if we're in here when the socket closes??
+ */
static void
smb_data_callback(struct sock *sk, int len)
{
- struct socket *sock = sk->socket;
+ struct socket *socket = sk->socket;
+ unsigned char peek_buf[4];
+ int result;
+ unsigned long fs;
+
+ fs = get_fs();
+ set_fs(get_ds());
- if (!sk->dead)
+ while (1)
{
- unsigned char peek_buf[4];
- int result;
- unsigned long fs;
+ if (sk->dead)
+ {
+ printk("smb_data_callback: sock dead!\n");
+ return;
+ }
+ result = _recvfrom(socket, (void *) peek_buf, 1,
+ MSG_PEEK | MSG_DONTWAIT);
+ if (result == -EAGAIN)
+ break;
+ if (peek_buf[0] != 0x85)
+ break;
- fs = get_fs();
- set_fs(get_ds());
+ /* got SESSION KEEP ALIVE */
+ result = _recvfrom(socket, (void *) peek_buf, 4,
+ MSG_DONTWAIT);
- result = _recvfrom(sock, (void *) peek_buf, 1,
- MSG_PEEK | MSG_DONTWAIT);
+ pr_debug("smb_data_callback: got SESSION KEEPALIVE\n");
- while ((result != -EAGAIN) && (peek_buf[0] == 0x85))
- {
- /* got SESSION KEEP ALIVE */
- result = _recvfrom(sock, (void *) peek_buf, 4,
- MSG_DONTWAIT);
-
- pr_debug("smb_data_callback: got SESSION KEEPALIVE\n");
-
- if (result == -EAGAIN)
- {
- break;
- }
- result = _recvfrom(sock, (void *) peek_buf, 1,
- MSG_PEEK | MSG_DONTWAIT);
- }
- set_fs(fs);
+ if (result == -EAGAIN)
+ break;
+ }
+ set_fs(fs);
- if (result != -EAGAIN)
- {
- wake_up_interruptible(sk->sleep);
- }
+ if (result != -EAGAIN)
+ {
+ wake_up_interruptible(sk->sleep);
}
}
-int
-smb_catch_keepalive(struct smb_sb_info *server)
+static struct socket *
+server_sock(struct smb_sb_info *server)
{
struct file *file;
struct inode *inode;
- struct socket *sock;
- struct sock *sk;
- if ((server == NULL)
- || ((file = server->sock_file) == NULL)
- || ((inode = file->f_dentry->d_inode) == NULL)
- || (!S_ISSOCK(inode->i_mode)))
- {
- pr_debug("smb_catch_keepalive: did not get valid server!\n");
- server->data_ready = NULL;
- return -EINVAL;
- }
- sock = &(inode->u.socket_i);
+ if (server &&
+ (file = server->sock_file) &&
+ (inode = file->f_dentry->d_inode) &&
+ S_ISSOCK(inode->i_mode) &&
+ inode->u.socket_i.type == SOCK_STREAM)
+ return &(inode->u.socket_i);
+ return NULL;
+}
- if (sock->type != SOCK_STREAM)
+int
+smb_catch_keepalive(struct smb_sb_info *server)
+{
+ struct socket *socket;
+ struct sock *sk;
+ void *data_ready;
+ int error;
+
+ error = -EINVAL;
+ socket = server_sock(server);
+ if (!socket)
{
- pr_debug("smb_catch_keepalive: did not get SOCK_STREAM\n");
+ printk("smb_catch_keepalive: did not get valid server!\n");
server->data_ready = NULL;
- return -EINVAL;
+ goto out;
}
- sk = sock->sk;
+ sk = socket->sk;
if (sk == NULL)
{
pr_debug("smb_catch_keepalive: sk == NULL");
server->data_ready = NULL;
- return -EINVAL;
+ goto out;
}
pr_debug("smb_catch_keepalive.: sk->d_r = %x, server->d_r = %x\n",
(unsigned int) (sk->data_ready),
(unsigned int) (server->data_ready));
- if (sk->data_ready == smb_data_callback)
+ /*
+ * Install the callback atomically to avoid races ...
+ */
+ data_ready = xchg(&sk->data_ready, smb_data_callback);
+ if (data_ready != smb_data_callback)
{
+ server->data_ready = data_ready;
+ error = 0;
+ } else
printk(KERN_ERR "smb_catch_keepalive: already done\n");
- return -EINVAL;
- }
- server->data_ready = sk->data_ready;
- sk->data_ready = smb_data_callback;
- return 0;
+out:
+ return error;
}
int
smb_dont_catch_keepalive(struct smb_sb_info *server)
{
- struct file *file;
- struct inode *inode;
- struct socket *sock;
+ struct socket *socket;
struct sock *sk;
+ void * data_ready;
+ int error;
- if ((server == NULL)
- || ((file = server->sock_file) == NULL)
- || ((inode = file->f_dentry->d_inode) == NULL)
- || (!S_ISSOCK(inode->i_mode)))
+ error = -EINVAL;
+ socket = server_sock(server);
+ if (!socket)
{
- printk("smb_dont_catch_keepalive: "
- "did not get valid server!\n");
- return -EINVAL;
- }
- sock = &(inode->u.socket_i);
-
- if (sock->type != SOCK_STREAM)
- {
- printk("smb_dont_catch_keepalive: did not get SOCK_STREAM\n");
- return -EINVAL;
+ printk("smb_dont_catch_keepalive: did not get valid server!\n");
+ goto out;
}
- sk = sock->sk;
+ sk = socket->sk;
if (sk == NULL)
{
printk("smb_dont_catch_keepalive: sk == NULL");
- return -EINVAL;
+ goto out;
}
+
+ /* Is this really an error?? */
if (server->data_ready == NULL)
{
printk("smb_dont_catch_keepalive: "
"server->data_ready == NULL\n");
- return -EINVAL;
- }
- if (sk->data_ready != smb_data_callback)
- {
- printk("smb_dont_catch_keepalive: "
- "sk->data_callback != smb_data_callback\n");
- return -EINVAL;
+ goto out;
}
pr_debug("smb_dont_catch_keepalive: sk->d_r = %x, server->d_r = %x\n",
(unsigned int) (sk->data_ready),
(unsigned int) (server->data_ready));
- sk->data_ready = server->data_ready;
+ /*
+ * Restore the original callback atomically to avoid races ...
+ */
+ data_ready = xchg(&sk->data_ready, server->data_ready);
server->data_ready = NULL;
- return 0;
+ if (data_ready != smb_data_callback)
+ {
+ printk("smb_dont_catch_keepalive: "
+ "sk->data_callback != smb_data_callback\n");
+ }
+ error = 0;
+out:
+ return error;
+}
+
+/*
+ * Called with the server locked.
+ */
+void
+smb_close_socket(struct smb_sb_info *server)
+{
+ struct file * file = server->sock_file;
+
+ if (file)
+ {
+ struct socket * socket = server_sock(server);
+
+ printk("smb_close_socket: closing socket %p\n", socket);
+ /*
+ * We need a way to check for tasks running the callback!
+ */
+ if (socket->sk->data_ready == smb_data_callback)
+ printk("smb_close_socket: still catching keepalives!\n");
+
+ server->sock_file = NULL;
+ close_fp(file);
+ }
}
static int
-smb_send_raw(struct socket *sock, unsigned char *source, int length)
+smb_send_raw(struct socket *socket, unsigned char *source, int length)
{
int result;
int already_sent = 0;
while (already_sent < length)
{
- result = _send(sock,
+ result = _send(socket,
(void *) (source + already_sent),
length - already_sent);
@@ -245,14 +284,14 @@ smb_send_raw(struct socket *sock, unsigned char *source, int length)
}
static int
-smb_receive_raw(struct socket *sock, unsigned char *target, int length)
+smb_receive_raw(struct socket *socket, unsigned char *target, int length)
{
int result;
int already_read = 0;
while (already_read < length)
{
- result = _recvfrom(sock,
+ result = _recvfrom(socket,
(void *) (target + already_read),
length - already_read, 0);
@@ -272,7 +311,7 @@ smb_receive_raw(struct socket *sock, unsigned char *target, int length)
}
static int
-smb_get_length(struct socket *sock, unsigned char *header)
+smb_get_length(struct socket *socket, unsigned char *header)
{
int result;
unsigned char peek_buf[4];
@@ -281,7 +320,7 @@ smb_get_length(struct socket *sock, unsigned char *header)
re_recv:
fs = get_fs();
set_fs(get_ds());
- result = smb_receive_raw(sock, peek_buf, 4);
+ result = smb_receive_raw(socket, peek_buf, 4);
set_fs(fs);
if (result < 0)
@@ -312,21 +351,6 @@ smb_get_length(struct socket *sock, unsigned char *header)
return smb_len(peek_buf);
}
-static struct socket *
-server_sock(struct smb_sb_info *server)
-{
- struct file *file;
- struct inode *inode;
-
- if (server == NULL)
- return NULL;
- if ((file = server->sock_file) == NULL)
- return NULL;
- if ((inode = file->f_dentry->d_inode) == NULL)
- return NULL;
- return &(inode->u.socket_i);
-}
-
/*
* smb_receive
* fs points to the correct segment
@@ -334,12 +358,12 @@ server_sock(struct smb_sb_info *server)
static int
smb_receive(struct smb_sb_info *server)
{
- struct socket *sock = server_sock(server);
+ struct socket *socket = server_sock(server);
int len;
int result;
unsigned char peek_buf[4];
- len = smb_get_length(sock, peek_buf);
+ len = smb_get_length(socket, peek_buf);
if (len < 0)
{
@@ -352,6 +376,7 @@ smb_receive(struct smb_sb_info *server)
pr_debug("smb_receive: Increase packet size from %d to %d\n",
server->packet_size, len + 4);
smb_vfree(server->packet);
+ server->packet = 0;
server->packet_size = 0;
server->packet = smb_vmalloc(len + 4);
if (server->packet == NULL)
@@ -361,7 +386,7 @@ smb_receive(struct smb_sb_info *server)
server->packet_size = len + 4;
}
memcpy(server->packet, peek_buf, 4);
- result = smb_receive_raw(sock, server->packet + 4, len);
+ result = smb_receive_raw(socket, server->packet + 4, len);
if (result < 0)
{
@@ -371,11 +396,10 @@ smb_receive(struct smb_sb_info *server)
server->rcls = *(server->packet+9);
server->err = WVAL(server->packet, 11);
- if (server->rcls != 0)
- {
- pr_debug("smb_receive: rcls=%d, err=%d\n",
- server->rcls, server->err);
- }
+#ifdef SMBFS_DEBUG_VERBOSE
+if (server->rcls != 0)
+printk("smb_receive: rcls=%d, err=%d\n", server->rcls, server->err);
+#endif
return result;
}
@@ -469,6 +493,13 @@ smb_receive_trans2(struct smb_sb_info *server,
total_data = WVAL(inbuf, smb_tdrcnt);
total_param = WVAL(inbuf, smb_tprcnt);
+#ifdef SMBFS_PARANOIA
+if ((data_len >= total_data || param_len >= total_param) &&
+ !(data_len >= total_data && param_len >= total_param))
+printk("smb_receive_trans2: dlen=%d, tdata=%d, plen=%d, tlen=%d\n",
+data_len, total_data, param_len, total_param);
+#endif
+ /* shouldn't this be an OR test? don't want to overrun */
if ((data_len >= total_data) && (param_len >= total_param))
{
break;
@@ -477,11 +508,9 @@ smb_receive_trans2(struct smb_sb_info *server,
{
goto fail;
}
+ result = -EIO;
if (server->rcls != 0)
- {
- result = -EIO;
goto fail;
- }
}
*ldata = data_len;
*lparam = param_len;
@@ -496,32 +525,33 @@ smb_receive_trans2(struct smb_sb_info *server,
return result;
}
+/*
+ * Called with the server locked
+ */
int
smb_request(struct smb_sb_info *server)
{
unsigned long old_mask;
unsigned long fs;
int len, result;
+ unsigned char *buffer;
- unsigned char *buffer = (server == NULL) ? NULL : server->packet;
+ result = -EBADF;
+ if (!server) /* this can't happen */
+ goto bad_no_server;
+
+ buffer = server->packet;
+ if (!buffer)
+ goto bad_no_packet;
- if (buffer == NULL)
- {
- pr_debug("smb_request: Bad server!\n");
- return -EBADF;
- }
+ result = -EIO;
if (server->state != CONN_VALID)
- {
- return -EIO;
- }
+ goto bad_no_conn;
+
if ((result = smb_dont_catch_keepalive(server)) != 0)
- {
- server->state = CONN_INVALID;
- smb_invalidate_inodes(server);
- return result;
- }
- len = smb_len(buffer) + 4;
+ goto bad_conn;
+ len = smb_len(buffer) + 4;
pr_debug("smb_request: len = %d cmd = 0x%X\n", len, buffer[8]);
old_mask = current->blocked;
@@ -544,17 +574,31 @@ smb_request(struct smb_sb_info *server)
int result2 = smb_catch_keepalive(server);
if (result2 < 0)
{
+ printk("smb_request: catch keepalive failed\n");
result = result2;
}
}
if (result < 0)
- {
- server->state = CONN_INVALID;
- smb_invalidate_inodes(server);
- }
- pr_debug("smb_request: result = %d\n", result);
+ goto bad_conn;
+out:
+ pr_debug("smb_request: result = %d\n", result);
return result;
+
+bad_conn:
+ printk("smb_request: result %d, setting invalid\n", result);
+ server->state = CONN_INVALID;
+ smb_invalidate_inodes(server);
+ goto out;
+bad_no_server:
+ printk("smb_request: no server!\n");
+ goto out;
+bad_no_packet:
+ printk("smb_request: no packet!\n");
+ goto out;
+bad_no_conn:
+ printk("smb_request: connection %d not valid!\n", server->state);
+ goto out;
}
#define ROUND_UP(x) (((x)+3) & ~3)
@@ -629,11 +673,11 @@ smb_send_trans2(struct smb_sb_info *server, __u16 trans2_command,
iov[3].iov_len = ldata;
err = scm_send(sock, &msg, &scm);
- if (err < 0)
- return err;
-
- err = sock->ops->sendmsg(sock, &msg, packet_length, &scm);
- scm_destroy(&scm);
+ if (err >= 0)
+ {
+ err = sock->ops->sendmsg(sock, &msg, packet_length, &scm);
+ scm_destroy(&scm);
+ }
return err;
}
@@ -655,16 +699,19 @@ smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command,
pr_debug("smb_trans2_request: com=%d, ld=%d, lp=%d\n",
trans2_command, ldata, lparam);
+ /*
+ * These are initialized in smb_request_ok, but not here??
+ */
+ server->rcls = 0;
+ server->err = 0;
+
+ result = -EIO;
if (server->state != CONN_VALID)
- {
- return -EIO;
- }
+ goto out;
+
if ((result = smb_dont_catch_keepalive(server)) != 0)
- {
- server->state = CONN_INVALID;
- smb_invalidate_inodes(server);
- return result;
- }
+ goto bad_conn;
+
old_mask = current->blocked;
current->blocked |= ~(_S(SIGKILL) | _S(SIGSTOP));
fs = get_fs();
@@ -691,11 +738,15 @@ smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command,
}
}
if (result < 0)
- {
- server->state = CONN_INVALID;
- smb_invalidate_inodes(server);
- }
+ goto bad_conn;
pr_debug("smb_trans2_request: result = %d\n", result);
+out:
return result;
+
+bad_conn:
+ printk("smb_trans2_request: connection bad, setting invalid\n");
+ server->state = CONN_INVALID;
+ smb_invalidate_inodes(server);
+ goto out;
}
diff --git a/fs/sysv/fsync.c b/fs/sysv/fsync.c
index df3b948df..96b0649a4 100644
--- a/fs/sysv/fsync.c
+++ b/fs/sysv/fsync.c
@@ -178,9 +178,10 @@ static int sync_tindirect(struct inode *inode, unsigned long *tiblockp, int conv
return err;
}
-int sysv_sync_file(struct inode * inode, struct file * file)
+int sysv_sync_file(struct file * file, struct dentry *dentry)
{
int wait, err = 0;
+ struct inode *inode = dentry->d_inode;
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
S_ISLNK(inode->i_mode)))