alistair23-linux/fs/ncpfs/file.c
Andi Kleen 9465efc9e9 Remove BKL from remote_llseek v2
- Replace remote_llseek with generic_file_llseek_unlocked (to force compilation
failures in all users)
- Change all users to either use generic_file_llseek_unlocked directly or
take the BKL around. I changed the file systems who don't use the BKL
for anything (CIFS, GFS) to call it directly. NCPFS and SMBFS and NFS
take the BKL, but explicitely in their own source now.

I moved them all over in a single patch to avoid unbisectable sections.

Open problem: 32bit kernels can corrupt fpos because its modification
is not atomic, but they can do that anyways because there's other paths who
modify it without BKL.

Do we need a special lock for the pos/f_version = 0 checks?

Trond says the NFS BKL is likely not needed, but keep it for now
until his full audit.

v2: Use generic_file_llseek_unlocked instead of remote_llseek_unlocked
    and factor duplicated code (suggested by hch)

Cc: Trond.Myklebust@netapp.com
Cc: swhiteho@redhat.com
Cc: sfrench@samba.org
Cc: vandrove@vc.cvut.cz

Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Jonathan Corbet <corbet@lwn.net>
2008-07-02 15:06:27 -06:00

312 lines
6.9 KiB
C

/*
* file.c
*
* Copyright (C) 1995, 1996 by Volker Lendecke
* Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
*
*/
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/time.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>
#include <linux/ncp_fs.h>
#include "ncplib_kernel.h"
static int ncp_fsync(struct file *file, struct dentry *dentry, int datasync)
{
return 0;
}
/*
* Open a file with the specified read/write mode.
*/
int ncp_make_open(struct inode *inode, int right)
{
int error;
int access;
error = -EINVAL;
if (!inode) {
printk(KERN_ERR "ncp_make_open: got NULL inode\n");
goto out;
}
DPRINTK("ncp_make_open: opened=%d, volume # %u, dir entry # %u\n",
atomic_read(&NCP_FINFO(inode)->opened),
NCP_FINFO(inode)->volNumber,
NCP_FINFO(inode)->dirEntNum);
error = -EACCES;
mutex_lock(&NCP_FINFO(inode)->open_mutex);
if (!atomic_read(&NCP_FINFO(inode)->opened)) {
struct ncp_entry_info finfo;
int result;
/* tries max. rights */
finfo.access = O_RDWR;
result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
inode, NULL, OC_MODE_OPEN,
0, AR_READ | AR_WRITE, &finfo);
if (!result)
goto update;
/* RDWR did not succeeded, try readonly or writeonly as requested */
switch (right) {
case O_RDONLY:
finfo.access = O_RDONLY;
result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
inode, NULL, OC_MODE_OPEN,
0, AR_READ, &finfo);
break;
case O_WRONLY:
finfo.access = O_WRONLY;
result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
inode, NULL, OC_MODE_OPEN,
0, AR_WRITE, &finfo);
break;
}
if (result) {
PPRINTK("ncp_make_open: failed, result=%d\n", result);
goto out_unlock;
}
/*
* Update the inode information.
*/
update:
ncp_update_inode(inode, &finfo);
atomic_set(&NCP_FINFO(inode)->opened, 1);
}
access = NCP_FINFO(inode)->access;
PPRINTK("ncp_make_open: file open, access=%x\n", access);
if (access == right || access == O_RDWR) {
atomic_inc(&NCP_FINFO(inode)->opened);
error = 0;
}
out_unlock:
mutex_unlock(&NCP_FINFO(inode)->open_mutex);
out:
return error;
}
static ssize_t
ncp_file_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
struct dentry *dentry = file->f_path.dentry;
struct inode *inode = dentry->d_inode;
size_t already_read = 0;
off_t pos;
size_t bufsize;
int error;
void* freepage;
size_t freelen;
DPRINTK("ncp_file_read: enter %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
if (!ncp_conn_valid(NCP_SERVER(inode)))
return -EIO;
pos = *ppos;
if ((ssize_t) count < 0) {
return -EINVAL;
}
if (!count)
return 0;
if (pos > inode->i_sb->s_maxbytes)
return 0;
if (pos + count > inode->i_sb->s_maxbytes) {
count = inode->i_sb->s_maxbytes - pos;
}
error = ncp_make_open(inode, O_RDONLY);
if (error) {
DPRINTK(KERN_ERR "ncp_file_read: open failed, error=%d\n", error);
return error;
}
bufsize = NCP_SERVER(inode)->buffer_size;
error = -EIO;
freelen = ncp_read_bounce_size(bufsize);
freepage = vmalloc(freelen);
if (!freepage)
goto outrel;
error = 0;
/* First read in as much as possible for each bufsize. */
while (already_read < count) {
int read_this_time;
size_t to_read = min_t(unsigned int,
bufsize - (pos % bufsize),
count - already_read);
error = ncp_read_bounce(NCP_SERVER(inode),
NCP_FINFO(inode)->file_handle,
pos, to_read, buf, &read_this_time,
freepage, freelen);
if (error) {
error = -EIO; /* NW errno -> Linux errno */
break;
}
pos += read_this_time;
buf += read_this_time;
already_read += read_this_time;
if (read_this_time != to_read) {
break;
}
}
vfree(freepage);
*ppos = pos;
file_accessed(file);
DPRINTK("ncp_file_read: exit %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
outrel:
ncp_inode_close(inode);
return already_read ? already_read : error;
}
static ssize_t
ncp_file_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
struct dentry *dentry = file->f_path.dentry;
struct inode *inode = dentry->d_inode;
size_t already_written = 0;
off_t pos;
size_t bufsize;
int errno;
void* bouncebuffer;
DPRINTK("ncp_file_write: enter %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
if (!ncp_conn_valid(NCP_SERVER(inode)))
return -EIO;
if ((ssize_t) count < 0)
return -EINVAL;
pos = *ppos;
if (file->f_flags & O_APPEND) {
pos = inode->i_size;
}
if (pos + count > MAX_NON_LFS && !(file->f_flags&O_LARGEFILE)) {
if (pos >= MAX_NON_LFS) {
return -EFBIG;
}
if (count > MAX_NON_LFS - (u32)pos) {
count = MAX_NON_LFS - (u32)pos;
}
}
if (pos >= inode->i_sb->s_maxbytes) {
if (count || pos > inode->i_sb->s_maxbytes) {
return -EFBIG;
}
}
if (pos + count > inode->i_sb->s_maxbytes) {
count = inode->i_sb->s_maxbytes - pos;
}
if (!count)
return 0;
errno = ncp_make_open(inode, O_WRONLY);
if (errno) {
DPRINTK(KERN_ERR "ncp_file_write: open failed, error=%d\n", errno);
return errno;
}
bufsize = NCP_SERVER(inode)->buffer_size;
already_written = 0;
bouncebuffer = vmalloc(bufsize);
if (!bouncebuffer) {
errno = -EIO; /* -ENOMEM */
goto outrel;
}
while (already_written < count) {
int written_this_time;
size_t to_write = min_t(unsigned int,
bufsize - (pos % bufsize),
count - already_written);
if (copy_from_user(bouncebuffer, buf, to_write)) {
errno = -EFAULT;
break;
}
if (ncp_write_kernel(NCP_SERVER(inode),
NCP_FINFO(inode)->file_handle,
pos, to_write, bouncebuffer, &written_this_time) != 0) {
errno = -EIO;
break;
}
pos += written_this_time;
buf += written_this_time;
already_written += written_this_time;
if (written_this_time != to_write) {
break;
}
}
vfree(bouncebuffer);
file_update_time(file);
*ppos = pos;
if (pos > inode->i_size) {
inode->i_size = pos;
}
DPRINTK("ncp_file_write: exit %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
outrel:
ncp_inode_close(inode);
return already_written ? already_written : errno;
}
static int ncp_release(struct inode *inode, struct file *file) {
if (ncp_make_closed(inode)) {
DPRINTK("ncp_release: failed to close\n");
}
return 0;
}
static loff_t ncp_remote_llseek(struct file *file, loff_t offset, int origin)
{
loff_t ret;
lock_kernel();
ret = generic_file_llseek_unlocked(file, offset, origin);
unlock_kernel();
return ret;
}
const struct file_operations ncp_file_operations =
{
.llseek = ncp_remote_llseek,
.read = ncp_file_read,
.write = ncp_file_write,
.ioctl = ncp_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ncp_compat_ioctl,
#endif
.mmap = ncp_mmap,
.release = ncp_release,
.fsync = ncp_fsync,
};
const struct inode_operations ncp_file_inode_operations =
{
.setattr = ncp_notify_change,
};