+++ /dev/null
-A Brief Overview of the Virtual File System
-===========================================
- by Benjamin LaHaise (blah@dot.superaje.com)
-
-No one else seems to be writing this, so here's a quick description of what
-I've learned while writing lofs.
-
-The VFS is relatively simple, but it is nice not to have to browse through
-pages of code to determine what is expected when writing a filesystem.
-This document is meant to help anyone attempting such a feat, and to clarify
-a few important points and dependencies.
-
-register_filesystem (struct file_system_type *fstype)
-=====================================================
-
-All filesystems are created equal, or at least they start out that way.
-A filesystem, whether in module form or linked into the kernel, needs to add
-itself to the table of filesystems by calling register_filesystem with an
-initialized file_system_type structure. Any further functions of the
-filesystem are accessed through the following function tables:
-
-
-struct file_system_type
-=======================
-
- struct super_block *(*read_super) (struct super_block *sb, void *options, int silent);
-
- This is the entry point of all filesystems. If the filesystem succeeds
- in mounting itself, sb should be returned, otherwise NULL. options is
- a pointer to a maximum of PAGE_SIZE-1 bytes of options, typically a zero
- terminated string passed from mount. This page is freed after read_super
- returns, so do not use any pointers into it.
-
- This routine _must_ set the s_op member of sb to point to a valid
- super_operations structure.
-
- const char *name;
-
- Name points to a string that the system will know the filesystem by.
-
- int requires_dev;
-
- Set this flag to 1 if the filesystem requires a block device to be mounted
- on.
-
- struct file_system_type * next;
-
- This field points to the next file_system_type that is present in the system,
- and should be initialized to NULL.
-
-struct super_operations
-=======================
-
-The super_operations structure is found through the s_op member of the
-super_block structure.
-
- void (*read_inode) (struct inode *inode);
- [optional - doesn't quite make sense]
- read_inode is called by the VFS when iget is called requesting an inode
- not already present in the inode table. i_ino is set to the number of the
- inode requested.
-
- The i_op member of inode should be set to a valid inode_operations
- structure. Typically filesystems have separate inode_operations for
- directories, files and symlinks. i_op can be NULL.
-
- int (*notify_change) (struct inode *, struct iattr *);
- [optional]
- void (*write_inode) (struct inode *);
- [optional]
-
- int (*put_inode) (struct inode *inode);
- [optional]
- put_inode is called by the VFS when the last instance of inode is released
- with a call to iput. The only special consideration that should be made
- is that iget may reuse inode without calling read_inode unless clear_inode
- is called. put_inode MUST return 1 if it called clear_inode on the inode,
- otherwise zero.
-
- void (*put_super) (struct super_block *);
- [optional]
- void (*write_super) (struct super_block *);
- [optional]
- void (*statfs) (struct super_block *, struct statfs *, int);
- [optional]
- int (*remount_fs) (struct super_block *, int *, char *);
- [optional]
-
-
-struct inode_operations
-=======================
-
- struct file_operations * default_file_ops;
- [mandatory]
- All inode_operations structures must have default_file_ops pointing to
- a valid file_operations structure.
-
- int (*create) (struct inode *,const char *,int,int,struct inode **);
- [optional]
-
- int (*lookup) (struct inode *dir, const char *name, int len, struct inode **result);
- [optional]
- lookup is called when the VFS wishes to have the filesystem resolve a name
- into an inode. Dir is a directory on the filesystem that--we hope--contains
- the zero-terminated string name (length len). A return value of zero indicates
- that there is a valid inode stored in *result.
-
-*** Note: lofs assumes that any filesystem returns an inode within the filesystem
- for all directory inodes. Therefore, __iget(sb,ino,0) should be used to fetch
- the inode in a filesystem's lookup routine.
-
- int (*link) (struct inode *,struct inode *,const char *,int);
- [optional]
- int (*unlink) (struct inode *,const char *,int);
- [optional]
- int (*symlink) (struct inode *,const char *,int,const char *);
- [optional]
- int (*mkdir) (struct inode *,const char *,int,int);
- [optional]
- int (*rmdir) (struct inode *,const char *,int);
- [optional]
- int (*mknod) (struct inode *,const char *,int,int,int);
- [optional]
- int (*rename) (struct inode *,const char *,int,struct inode *,const char *,int, int);
- [optional]
-
- int (*readlink) (struct inode *inode, char *buf, int len);
- [optional]
- readlink is called by the VFS to read the contents of a symbolic link.
- inode is an inode that meets the S_ISLNK test, and buf points to a buffer
- of len bytes.
-
- int (*follow_link) (struct inode *,struct inode *,int,int,struct inode **);
- [optional]
- follow_link must be implemented if readlink is implemented.
- Note that follow_link can return a different inode than a
- lookup_dentry() on the result of readlink() would return.
- The proc filesystem, in particular, uses this feature heavily.
- For most user filesystems, however, follow_link() and readlink()
- should return consistent results.
-
- int (*readpage) (struct inode *, struct page *); [optional]
- int (*writepage) (struct inode *, struct page *); [mandatory with readpage]
-
- In order for files to be mmap'd, readpage and writepage are required.
- A filesystem can use generic_readpage/writepage if it supports the bmap
- function. Otherwise, a custom version must be written.
-
- int (*bmap) (struct inode *,int);
- [optional]
- void (*truncate) (struct inode *);
- [optional]
- int (*permission) (struct inode *, int);
- [optional]
- int (*smap) (struct inode *,int);
- [optional]
-
-struct file_operations
-======================
-
- int (*lseek) (struct inode *, struct file *, off_t, int);
- int (*read) (struct inode *, struct file *, char *, int);
- int (*write) (struct inode *, struct file *, const char *, int);
- int (*readdir) (struct inode *, struct file *, void *, filldir_t);
- unsigned int (*poll) (struct file *, poll_table *);
- int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
- int (*mmap) (struct inode *, struct file *, struct vm_area_struct *);
- int (*open) (struct inode *, struct file *);
- void (*release) (struct inode *, struct file *);
- int (*fsync) (struct inode *, struct file *);
- int (*fasync) (struct inode *, struct file *, int);
- int (*check_media_change) (kdev_t dev);
- int (*revalidate) (kdev_t dev);
-
VERSION = 2
PATCHLEVEL = 1
-SUBLEVEL = 107
+SUBLEVEL = 108
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
.long SYMBOL_NAME(sys_capget)
.long SYMBOL_NAME(sys_capset) /* 185 */
.long SYMBOL_NAME(sys_sigaltstack)
+ .long SYMBOL_NAME(sys_sendfile)
- .rept NR_syscalls-186
+ .rept NR_syscalls-187
.long SYMBOL_NAME(sys_ni_syscall)
.endr
continue;
#endif
p += sprintf(p, "processor\t: %d\n"
- "CPU family\t: %c\n"
+ "cpu family\t: %c\n"
"model\t\t: %s\n"
"vendor_id\t: %s\n",
n,
"hlt_bug\t\t: %s\n"
"sep_bug\t\t: %s\n"
"f00f_bug\t: %s\n"
- "FPU\t\t: %s\n"
- "FPU_exception\t: %s\n"
- "CPUID level\t: %d\n"
+ "fpu\t\t: %s\n"
+ "fpu_exception\t: %s\n"
+ "cpuid level\t: %d\n"
"wp\t\t: %s\n"
"flags\t\t:",
c->fdiv_bug ? "yes" : "no",
* moment everything difficult is handled by the generic code.
*/
-#include <linux/config.h>
#include <asm/ptrace.h>
#include <linux/types.h>
#include <linux/init.h>
* This file contains the HP300-specific time handling code.
*/
-#include <linux/config.h>
#include <asm/ptrace.h>
#include <linux/types.h>
#include <linux/init.h>
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/linkage.h>
#include <linux/sched.h>
* -
*/
+#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
* via them as are assorted bits and bobs - eg rtc, adb.
*/
-#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
CONFIG_EXPERIMENTAL=y
CONFIG_MODULES=y
# CONFIG_MODVERSIONS is not set
-CONFIG_KERNELD=y
+CONFIG_KMOD=y
CONFIG_PCI=y
CONFIG_PCI_OLD_PROC=y
CONFIG_NET=y
CONFIG_EXPERIMENTAL=y
CONFIG_MODULES=y
CONFIG_MODVERSIONS=y
-CONFIG_KERNELD=y
+CONFIG_KMOD=y
CONFIG_PCI=y
CONFIG_PCI_OLD_PROC=y
CONFIG_NET=y
CONFIG_EXPERIMENTAL=y
CONFIG_MODULES=y
CONFIG_MODVERSIONS=y
-CONFIG_KERNELD=y
+CONFIG_KMOD=y
CONFIG_PCI=y
# CONFIG_PCI_OPTIMIZE is not set
CONFIG_PCI_OLD_PROC=y
void addpair(int fp, int un)
{
int i;
- unicode hu;
if ( un <= 0xfffe )
{
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
#endif
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
#endif
#ifdef __mc68000__
}
}
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
static void try_to_load(int fb)
{
char modname[16];
return -EINVAL;
if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)
return -EINVAL;
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
if (!registered_fb[con2fb.framebuffer])
try_to_load(con2fb.framebuffer);
#endif
int fbidx = GET_FB_IDX(inode->i_rdev);
struct fb_info *info;
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
if (!(info = registered_fb[fbidx]))
try_to_load(fbidx);
#endif
* - making it shorter - scr_readw are macros which expand in PRETTY long code
*/
-#include <linux/config.h>
-
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/errno.h>
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/delay.h>
* most of a2025 and sunlance with the aim of merging them, so the
* common code was pretty obvious.
*/
-#include <linux/config.h>
#include <linux/module.h>
-
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
/*****************************************************************************/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
{
int i, j, k, l;
float s;
- float c[40];
+ float c[44];
float min, max;
fprintf(f, "\n/*\n * hapn4800 specific tables\n */\n\n");
* Uses the generic 7990.c LANCE code.
*/
-#include <linux/config.h>
#include <linux/module.h>
-
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
static const char *version = "sktr.c: v1.01 08/29/97 by Christoph Goos\n";
-#include <linux/config.h>
#ifdef MODULE
#include <linux/module.h>
#include <linux/version.h>
#ifndef NCR53C9X_H
#define NCR53C9X_H
+#include <linux/config.h>
+
/* Macros for debugging messages */
/* #define DEBUG_ESP */
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/config.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
#endif
#include <asm/irq.h>
fcscount++;
PLND(("%d channels online\n", fcscount))
if (!fcscount) {
-#if defined(MODULE) && defined(CONFIG_FC4_SOC_MODULE) && defined(CONFIG_KERNELD)
+#if defined(MODULE) && defined(CONFIG_FC4_SOC_MODULE) && defined(CONFIG_KMOD)
request_module("soc");
for_each_online_fc_channel(fc)
*
********************************************************************/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/malloc.h>
*
********************************************************************/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/types.h>
#ifndef __MSND_CLASSIC_H
#define __MSND_CLASSIC_H
+#include <linux/config.h>
+
#define DSP_NUMIO 0x10
#define HP_MEMM 0x08
*
********************************************************************/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/types.h>
#ifndef __MSND_PINNACLE_H
#define __MSND_PINNACLE_H
+#include <linux/config.h>
+
#define DSP_NUMIO 0x08
#define HP_DSPR 0x04
* locking at some point in 2.3.x.
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/types.h>
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#if defined(__BIG_ENDIAN)
d = *cdat1++<<24 | *cdat2++<<16 | *cdat3++<<8 | *cdat4++;
#elif defined(__LITTLE_ENDIAN)
- d = *cdat1++ | *cdat2++<<8 | *cdat3++<<16 | *cdat4++<<32);
+ d = *cdat1++ | *cdat2++<<8 | *cdat3++<<16 | *cdat4++<<24;
#else
#error FIXME: No endianness??
#endif
* more details.
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/console.h>
* more details.
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/console.h>
* more details.
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/console.h>
* No! -- Jes
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
*
*/
-#include <linux/config.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/sysctl.h>
#ifdef __SMP__
len = sprintf(buffer,
- "CPU %u %u %u %lu\n",
+ "cpu %u %u %u %lu\n",
kstat.cpu_user,
kstat.cpu_nice,
kstat.cpu_system,
current->state = TASK_INTERRUPTIBLE;
for (fdpnt = fds, j = 0; j < nfds; j++, fdpnt++) {
+ int fd;
unsigned int mask;
- struct file * file;
- mask = POLLNVAL;
- /* poll_wait increments f_count if needed */
- file = fcheck(fdpnt->fd);
- if (file != NULL) {
- mask = DEFAULT_POLLMASK;
- if (file->f_op && file->f_op->poll)
- mask = file->f_op->poll(file, wait);
- mask &= fdpnt->events | POLLERR | POLLHUP;
- }
- if (mask) {
- wait = NULL;
- count++;
+ mask = 0;
+ fd = fdpnt->fd;
+ if (fd >= 0) {
+ /* poll_wait increments f_count if needed */
+ struct file * file = fcheck(fd);
+ mask = POLLNVAL;
+ if (file != NULL) {
+ mask = DEFAULT_POLLMASK;
+ if (file->f_op && file->f_op->poll)
+ mask = file->f_op->poll(file, wait);
+ mask &= fdpnt->events | POLLERR | POLLHUP;
+ }
+ if (mask) {
+ wait = NULL;
+ count++;
+ }
}
fdpnt->revents = mask;
}
Fri Jan 23 1998 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
* inode.c: corrected 1 track offset setting (in sb->sv_block_base).
- Originally it was overriden (by setting to zero)
+ Originally it was overridden (by setting to zero)
in detected_[xenix,sysv4,sysv2,coherent]. Thanks
to Andrzej Krzysztofowicz <ankry@mif.pg.gda.pl>
for identifying the problem.
#
-# Makefile for the Linux SystemV/Coherent-filesystem routines.
+# Makefile for the Linux SystemV/Coherent filesystem routines.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
+# unless it's something special (not a .c file).
#
-# Note 2! The CFLAGS definitions are now in the main makefile...
+# Note 2! The CFLAGS definitions are now in the main makefile.
O_TARGET := sysv.o
O_OBJS := ialloc.o balloc.o inode.o file.o dir.o symlink.o namei.o \
static ssize_t sysv_file_write(struct file *, const char *, size_t, loff_t *);
/*
- * We have mostly NULL's here: the current defaults are ok for
+ * We have mostly NULLs here: the current defaults are OK for
* the coh filesystem.
*/
static struct file_operations sysv_file_operations = {
return -EINVAL;
}
/*
- * ok, append may not work when many processes are writing at the same time
+ * OK, append may not work when many processes are writing at the same time
* but so what. That way leads to madness anyway.
* But we need to protect against simultaneous truncate as we may end up
* writing our data into blocks that have meanwhile been incorporated into
/*
* Truncate has the most races in the whole filesystem: coding it is
- * a pain in the a**. Especially as I don't do any locking...
+ * a pain in the a**, especially as I don't do any locking.
*
* The code may look a bit weird, but that's just because I've tried to
* handle things like file-size changes in a somewhat graceful manner.
#define __NR_capget 184
#define __NR_capset 185
#define __NR_sigaltstack 186
+#define __NR_sendfile 187
/* user-visible error numbers are in the range -1 - -122: see <asm-i386/errno.h> */
#ifndef _M68K_PGTABLE_H
#define _M68K_PGTABLE_H
+#include <linux/config.h>
#include <asm/setup.h>
#ifndef __ASSEMBLY__
#ifndef _LINUX_CONSOLE_COMPAT_H_
#define _LINUX_CONSOLE_COMPAT_H_
+#include <linux/config.h>
+
#undef video_num_columns
#undef video_num_lines
#undef video_size_row
*/
#define CUR_DEFAULT CUR_UNDERLINE
-#include <linux/config.h>
-
#define NPAR 16
struct vc_data {
* Interface between console.c, tty_io.c, vt.c, vc_screen.c and selection.c
*/
-#include <linux/config.h>
-
extern int sel_cons;
extern void clear_selection(void);
/* how to access screen memory */
-#include <linux/config.h>
-
static inline void scr_writew(unsigned short val, unsigned short *addr)
{
/* simply store the value in the "shadow screen" memory */
return page_cache;
}
+/*
+ * "descriptor" for what we're up to with a read.
+ * This allows us to use the same read code yet
+ * have multiple different users of the data that
+ * we read from a file.
+ *
+ * The simplest case just copies the data to user
+ * mode.
+ */
+typedef struct {
+ size_t written;
+ size_t count;
+ char * buf;
+ int error;
+} read_descriptor_t;
+
+typedef int (*read_actor_t)(read_descriptor_t *, const char *, unsigned long);
/*
* This is a generic file read routine, and uses the
* This is really ugly. But the goto's actually try to clarify some
* of the logic when it comes to error handling etc.
*/
-
-ssize_t generic_file_read(struct file * filp, char * buf,
- size_t count, loff_t *ppos)
+static void do_generic_file_read(struct file * filp, loff_t *ppos, read_descriptor_t * desc, read_actor_t actor)
{
struct dentry *dentry = filp->f_dentry;
struct inode *inode = dentry->d_inode;
- ssize_t error, read;
size_t pos, pgpos, page_cache;
int reada_ok;
int max_readahead = get_max_readahead(inode);
- if (!access_ok(VERIFY_WRITE, buf, count))
- return -EFAULT;
- if (!count)
- return 0;
- error = 0;
- read = 0;
page_cache = 0;
pos = *ppos;
* Then, at least MIN_READAHEAD if read ahead is ok,
* and at most MAX_READAHEAD in all cases.
*/
- if (pos + count <= (PAGE_SIZE >> 1)) {
+ if (pos + desc->count <= (PAGE_SIZE >> 1)) {
filp->f_ramax = 0;
} else {
unsigned long needed;
- needed = ((pos + count) & PAGE_MASK) - pgpos;
+ needed = ((pos + desc->count) & PAGE_MASK) - pgpos;
if (filp->f_ramax < needed)
filp->f_ramax = needed;
offset = pos & ~PAGE_MASK;
nr = PAGE_SIZE - offset;
- if (nr > count)
- nr = count;
if (nr > inode->i_size - pos)
nr = inode->i_size - pos;
- nr -= copy_to_user(buf, (void *) (page_address(page) + offset), nr);
- release_page(page);
- error = -EFAULT;
- if (!nr)
- break;
- buf += nr;
+
+ /*
+ * The actor routine returns how many bytes were actually used..
+ * NOTE! This may not be the same as how much of a user buffer
+ * we filled up (we may be padding etc), so we can only update
+ * "pos" here (the actor routine has to update the user buffer
+ * pointers and the remaining count).
+ */
+ nr = actor(desc, (const char *) (page_address(page) + offset), nr);
pos += nr;
- read += nr;
- count -= nr;
- if (count)
+ release_page(page);
+ if (nr && desc->count)
continue;
break;
}
*/
if (page_cache)
continue;
- error = -ENOMEM;
+ desc->error = -ENOMEM;
break;
}
if (reada_ok && filp->f_ramax > MIN_READAHEAD)
filp->f_ramax = MIN_READAHEAD;
- error = inode->i_op->readpage(filp, page);
- if (!error)
- goto found_page;
- release_page(page);
- break;
+ {
+ int error = inode->i_op->readpage(filp, page);
+ if (!error)
+ goto found_page;
+ desc->error = error;
+ release_page(page);
+ break;
+ }
page_read_error:
/*
* Try to re-read it _once_. We do this synchronously,
* because this happens only if there were errors.
*/
- error = inode->i_op->readpage(filp, page);
- if (!error) {
- wait_on_page(page);
- if (PageUptodate(page) && !PageError(page))
- goto success;
- error = -EIO; /* Some unspecified error occurred.. */
+ {
+ int error = inode->i_op->readpage(filp, page);
+ if (!error) {
+ wait_on_page(page);
+ if (PageUptodate(page) && !PageError(page))
+ goto success;
+ error = -EIO; /* Some unspecified error occurred.. */
+ }
+ desc->error = error;
+ release_page(page);
+ break;
}
- release_page(page);
- break;
}
*ppos = pos;
if (page_cache)
free_page(page_cache);
UPDATE_ATIME(inode);
- if (!read)
- read = error;
- return read;
+}
+
+static int file_read_actor(read_descriptor_t * desc, const char *area, unsigned long size)
+{
+ unsigned long left;
+ unsigned long count = desc->count;
+
+ if (size > count)
+ size = count;
+ left = __copy_to_user(desc->buf, area, size);
+ if (left) {
+ size -= left;
+ desc->error = -EFAULT;
+ }
+ desc->count = count - size;
+ desc->written += size;
+ desc->buf += size;
+ return size;
+}
+
+/*
+ * This is the "read()" routine for all filesystems
+ * that can use the page cache directly.
+ */
+ssize_t generic_file_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
+{
+ ssize_t retval;
+
+ retval = -EFAULT;
+ if (access_ok(VERIFY_WRITE, buf, count)) {
+ retval = 0;
+ if (count) {
+ read_descriptor_t desc;
+
+ desc.written = 0;
+ desc.count = count;
+ desc.buf = buf;
+ desc.error = 0;
+ do_generic_file_read(filp, ppos, &desc, file_read_actor);
+
+ retval = desc.written;
+ if (!retval)
+ retval = desc.error;
+ }
+ }
+ return retval;
+}
+
+static int file_send_actor(read_descriptor_t * desc, const char *area, unsigned long size)
+{
+ ssize_t written;
+ unsigned long count = desc->count;
+ struct file *file = (struct file *) desc->buf;
+ struct inode *inode = file->f_dentry->d_inode;
+
+ if (size > count)
+ size = count;
+ down(&inode->i_sem);
+ set_fs(KERNEL_DS);
+ written = file->f_op->write(file, area, size, &file->f_pos);
+ set_fs(USER_DS);
+ up(&inode->i_sem);
+ if (written < 0) {
+ desc->error = written;
+ written = 0;
+ }
+ desc->count = count - written;
+ desc->written += written;
+ return written;
+}
+
+asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, size_t count)
+{
+ ssize_t retval;
+ struct file * in_file, * out_file;
+ struct inode * in_inode, * out_inode;
+
+ lock_kernel();
+
+ /*
+ * Get input file, and verify that it is ok..
+ */
+ retval = -EBADF;
+ in_file = fget(in_fd);
+ if (!in_file)
+ goto out;
+ if (!(in_file->f_mode & FMODE_READ))
+ goto fput_in;
+ retval = -EINVAL;
+ in_inode = in_file->f_dentry->d_inode;
+ if (!in_inode)
+ goto fput_in;
+ if (!in_inode->i_op || !in_inode->i_op->readpage)
+ goto fput_in;
+ retval = locks_verify_area(FLOCK_VERIFY_READ, in_inode, in_file, in_file->f_pos, count);
+ if (retval)
+ goto fput_in;
+
+ /*
+ * Get output file, and verify that it is ok..
+ */
+ retval = -EBADF;
+ out_file = fget(out_fd);
+ if (!out_file)
+ goto fput_in;
+ if (!(out_file->f_mode & FMODE_WRITE))
+ goto fput_out;
+ retval = -EINVAL;
+ if (!out_file->f_op || !out_file->f_op->write)
+ goto fput_out;
+ out_inode = out_file->f_dentry->d_inode;
+ if (!out_inode)
+ goto fput_out;
+ retval = locks_verify_area(FLOCK_VERIFY_WRITE, out_inode, out_file, out_file->f_pos, count);
+ if (retval)
+ goto fput_out;
+
+ retval = 0;
+ if (count) {
+ read_descriptor_t desc;
+
+ desc.written = 0;
+ desc.count = count;
+ desc.buf = (char *) out_file;
+ desc.error = 0;
+ do_generic_file_read(in_file, &in_file->f_pos, &desc, file_send_actor);
+
+ retval = desc.written;
+ if (!retval)
+ retval = desc.error;
+ }
+
+
+fput_out:
+ fput(out_file);
+fput_in:
+ fput(in_file);
+out:
+ unlock_kernel();
+ return retval;
}
/*