/*
* Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
* Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU General Public License v.2.
*/
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/buffer_head.h>
#include <linux/pagemap.h>
#include <linux/uio.h>
#include <linux/blkdev.h>
#include <linux/mm.h>
#include <linux/smp_lock.h>
#include <linux/fs.h>
#include <linux/gfs2_ondisk.h>
#include <linux/ext2_fs.h>
#include <linux/crc32.h>
#include <linux/iflags.h>
#include <asm/uaccess.h>
#include "gfs2.h"
#include "lm_interface.h"
#include "incore.h"
#include "bmap.h"
#include "dir.h"
#include "glock.h"
#include "glops.h"
#include "inode.h"
#include "lm.h"
#include "log.h"
#include "meta_io.h"
#include "ops_file.h"
#include "ops_vm.h"
#include "quota.h"
#include "rgrp.h"
#include "trans.h"
#include "util.h"
#include "eaops.h"
/* "bad" is for NFS support */
struct filldir_bad_entry {
char *fbe_name;
unsigned int fbe_length;
uint64_t fbe_offset;
struct gfs2_inum fbe_inum;
unsigned int fbe_type;
};
struct filldir_bad {
struct gfs2_sbd *fdb_sbd;
struct filldir_bad_entry *fdb_entry;
unsigned int fdb_entry_num;
unsigned int fdb_entry_off;
char *fdb_name;
unsigned int fdb_name_size;
unsigned int fdb_name_off;
};
/* For regular, non-NFS */
struct filldir_reg {
struct gfs2_sbd *fdr_sbd;
int fdr_prefetch;
filldir_t fdr_filldir;
void *fdr_opaque;
};
/*
* Most fields left uninitialised to catch anybody who tries to
* use them. f_flags set to prevent file_accessed() from touching
* any other part of this. Its use is purely as a flag so that we
* know (in readpage()) whether or not do to locking.
*/
struct file gfs2_internal_file_sentinal = {
.f_flags = O_NOATIME|O_RDONLY,
};
static int gfs2_read_actor(read_descriptor_t *desc, struct page *page,
unsigned long offset, unsigned long size)
{
char *kaddr;
unsigned long count = desc->count;
if (size > count)
size = count;
kaddr = kmap(page);
memcpy(desc->arg.buf, kaddr + offset, size);
kunmap(page);
desc->count = count - size;
desc->written += size;
desc->arg.buf += size;
return size;
}
int gfs2_internal_read(struct gfs2_inode *ip, struct file_ra_state *ra_state,
char *buf, loff_t *pos, unsigned size)
{
struct inode *inode = ip->i_vnode;
read_descriptor_t desc;
desc.written = 0;
desc.arg.buf = buf;
desc.count = size;
desc.error = 0;
do_generic_mapping_read(inode->i_mapping, ra_state,
&gfs2_internal_file_sentinal, pos, &desc,
gfs2_read_actor);
return desc.written ? desc.written : desc.error;
}
/**
* gfs2_llseek - seek to a location in a file
* @file: the file
* @offset: the offset
* @origin: Where to seek from (SEEK_SET, SEEK_CUR, or SEEK_END)
*
* SEEK_END requires the glock for the file because it references the
* file's size.
*
* Returns: The new offset, or errno
*/
static loff_t gfs2_llseek(struct file *file, loff_t offset, int origin)
{
struct gfs2_inode *ip = file->f_mapping->host->u.generic_ip;
struct gfs2_holder i_gh;
loff_t error;
if (origin == 2) {
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY,
&i_gh);
if (!error) {
error = remote_llseek(file, offset, origin);
gfs2_glock_dq_uninit(&i_gh);
}
} else
error = remote_llseek(file, offset, origin);
return error;
}
static ssize_t gfs2_direct_IO_read(struct kiocb *iocb, const struct iovec *iov,
loff_t offset, unsigned long nr_segs)
{
struct file *file = iocb->ki_filp;
struct address_space *mapping = file->f_mapping;
ssize_t retval;
retval = filemap_write_and_wait(mapping);
if (retval == 0) {
retval = mapping->a_ops->direct_IO(READ, iocb, iov, offset,
nr_segs);
}