This is file_ops.c in view mode; [Download] [Up]
/*
* Name: file_ops.c
* Description: Functions that can be called from the NFS interface.
* Author: Christian Starkjohann <cs@hal.kph.tuwien.ac.at>
* Date: 1996-11-14
* Copyright: GNU-GPL
* Tabsize: 4
*/
#include <linux/autoconf.h>
#include <linux/fs.h>
#include <linux/affs_fs.h>
#include <linux/ext2_fs.h>
#include <linux/ext_fs.h>
#include <linux/hpfs_fs.h>
#include <linux/iso_fs.h>
#include <linux/minix_fs.h>
#include <linux/msdos_fs.h>
#include <linux/ncp_fs.h>
#include <linux/nfs_fs.h>
#include <linux/proc_fs.h>
#include <linux/smb_fs.h>
#include <linux/sysv_fs.h>
#include <linux/ufs_fs.h>
#include <linux/umsdos_fs.h>
#include <linux/xia_fs.h>
#include "my_defines.h"
#define DPRINTF(arg) if(debug_mode & DEBUG_FOPS) dprintf arg
extern void init_ntfs_fs(void);
#define MY_DEVICE 256
#define TRANSFER_BUFFER_SIZE 50000
/* ------------------------------------------------------------------------- */
extern void bzero(void *block, int size);
extern void *malloc(int size);
extern int my_blocksize;
/* ------------------------------------------------------------------------- */
struct super_block my_superblock;
static struct file_system_type *fs_types = NULL;
/* ------------------------------------------------------------------------- */
int register_filesystem(struct file_system_type *fstype)
{
DPRINTF(("registering filesystem %s\n", fstype->name));
fstype->next = fs_types;
fs_types = fstype;
return 0;
}
/* ------------------------------------------------------------------------- */
static void my_sync_super(void)
{
struct super_block *s = &my_superblock;
if(s->s_dirt && s->s_op->write_super != NULL){
s->s_op->write_super(s);
s->s_dirt = 0;
}
}
/* ------------------------------------------------------------------------- */
void my_sync(void)
{
my_sync_super();
my_sync_inodes();
my_sync_blocks();
}
/* ------------------------------------------------------------------------- */
static int is_valid_fs(struct file_system_type *fs)
{
struct super_block s;
bzero(&s, sizeof(s));
s.s_dev = MY_DEVICE;
s.s_flags = MS_RDONLY;
return fs->read_super(&s, "", 0) != 0;
}
/* ------------------------------------------------------------------------- */
char *valid_filesystems(void)
{
struct file_system_type *fs;
static char fslist[1024]; /* don't like static limits.... */
fslist[0] = 0;
for(fs=fs_types;fs!=NULL;fs=fs->next){
if(is_valid_fs(fs)){
if(fslist[0] != 0)
strcat(fslist, " ");
strcat(fslist, fs->name);
}
}
return fslist;
}
/* ------------------------------------------------------------------------- */
static int do_my_mount(struct file_system_type *fs, int flags, void *data, int *root_i)
{
struct super_block *s = &my_superblock;
DPRINTF(("do_my_mount(fs=%s, flags=0x%x)\n", fs->name, flags));
s->s_dev = MY_DEVICE;
s->s_flags = flags;
if (!fs->read_super(s, data, 0)){
return -1;
}
s->s_dev = MY_DEVICE;
s->s_covered = s->s_mounted; /* used for error output! should be NULL */
s->s_type = fs;
*root_i = s->s_mounted->i_ino;
if(s->s_op->write_super != NULL){
s->s_op->write_super(s);
s->s_dirt = 0;
}
my_sync();
return 0;
}
/* ------------------------------------------------------------------------- */
int my_mount(char *fsname, int options, void *data, int *root_inode)
{
struct file_system_type *fs;
bzero(&my_superblock, sizeof(my_superblock));
for(fs=fs_types;fs!=NULL;fs=fs->next){
if(strcmp(fsname, fs->name) == 0){
break;
}
}
if(fs != NULL){
return do_my_mount(fs, options & MNT_RONLY ? MS_RDONLY : 0,
data, root_inode);
}else{
printk("filesystem ->%s<- not found\n", fsname);
return -1;
}
}
/* ------------------------------------------------------------------------- */
int my_unmount(void)
{
struct super_block *sb = &my_superblock;
DPRINTF(("my_unmount()\n"));
iput(sb->s_mounted); /* free mounted inode */
my_sync();
if(sb->s_op && sb->s_op->put_super)
sb->s_op->put_super(sb);
my_sync_inodes();
my_sync_blocks();
return 0;
}
/* ------------------------------------------------------------------------- */
static int check_access(struct inode *inode, int mode)
{
int i, igid = inode->i_gid, iuid = inode->i_uid;
if(nfs_uid == 0){ /* root may do everything */
return 1;
}
if(id_is_fixed(IDBUF_USR))
iuid = fixed_id(IDBUF_USR);
if(id_is_fixed(IDBUF_GRP))
igid = fixed_id(IDBUF_GRP);
if(nfs_uid == iuid){ /* you may always access your own files */
return 1;
}
if(iuid == nfs_uid){
return (inode->i_mode & (mode << 6)) != 0;
}
for(i=0;i<nfs_gidslen;i++){
if(igid == nfs_gids[i]){
return (inode->i_mode & (mode << 3)) != 0;
}
}
return (inode->i_mode & mode) != 0;
}
/* ------------------------------------------------------------------------- */
#define CHECK_WRITE(inode, iputs) \
if(!check_access(inode, 2)){ \
error = -MY_NFSERR_ACCES; \
DPRINTF(("write access for %ld denied\n", inode->i_ino)); \
iputs; \
goto do_return; \
}
#define CHECK_READ(inode, iputs) \
if(!check_access(inode, 4)){ \
error = -MY_NFSERR_ACCES; \
DPRINTF(("read access for %ld denied\n", inode->i_ino)); \
iputs; \
goto do_return; \
}
#define CHECK_EXEC(inode, iputs) \
if(!check_access(inode, 1)){ \
error = -MY_NFSERR_ACCES; \
DPRINTF(("search access for %ld denied\n", inode->i_ino)); \
iputs; \
goto do_return; \
}
#define CHECK_ATTR(inode, iputs) \
{ int iuid = inode->i_uid; \
if(id_is_fixed(IDBUF_USR)) \
iuid = fixed_id(IDBUF_USR); \
if(nfs_uid != 0 && iuid != nfs_uid){ \
error = -MY_NFSERR_ACCES; \
DPRINTF(("chmod access for %ld denied\n", inode->i_ino)); \
iputs; \
goto do_return; \
} \
}
/* ------------------------------------------------------------------------- */
static void set_attr(struct inode *inode, my_attr_t *attr)
{
int i;
if(attr->mode != -1 && inode->i_mode != attr->mode){
inode->i_mode = (inode->i_mode & ~07777) | (attr->mode & 07777);
inode->i_dirt = 1;
}
if(nfs_uid == 0 && attr->uid != -1 && inode->i_uid != attr->uid){
inode->i_uid = attr->uid;
inode->i_dirt = 1;
}
if(attr->gid != -1 && inode->i_gid != attr->gid){
for(i=0;i<nfs_gidslen;i++){
if(nfs_gids[i] == attr->gid){
inode->i_gid = attr->gid;
inode->i_dirt = 1;
break;
}
}
}
if(attr->mtime != -1 && inode->i_mtime != attr->mtime){
inode->i_mtime = attr->mtime;
inode->i_dirt = 1;
}
}
/* ------------------------------------------------------------------------- */
static void get_attr(struct inode *inode, my_attr_t *attr)
{
attr->mode = inode->i_mode;
attr->nlink = inode->i_nlink;
attr->uid = inode->i_uid;
attr->gid = inode->i_gid;
attr->size = S_ISDIR(attr->mode) && inode->i_size == 0 ?
100000 : inode->i_size; /* fake a directory size */
attr->blocksize = inode->i_blksize > 0 ? inode->i_blksize : PAGE_SIZE;
attr->blocks = inode->i_blocks > 0 ?
inode->i_blocks : (attr->size + my_blocksize - 1) / my_blocksize;
attr->fileid = inode->i_ino;
attr->atime = inode->i_atime;
attr->mtime = inode->i_mtime;
attr->ctime = inode->i_ctime;
attr->rdev = inode->i_rdev;
}
/* ------------------------------------------------------------------------- */
#define FIND_INODE(inode, fh, ops, iputs) \
if((inode = iget(&my_superblock, fh)) == NULL){ \
DPRINTF(("invalid inode %d: not found in %s/%d\n",\
fh, __FILE__, __LINE__)); \
iputs; \
error = -MY_NFSERR_STALE; \
goto do_return; \
} \
if((ops = inode->i_op) == NULL){ \
printk("invalid inode %d: no inode operations in %s/%d\n",\
fh, __FILE__, __LINE__);\
iput(inode); \
iputs; \
error = -MY_NFSERR_STALE; \
goto do_return; \
}
#define VALIDATE(operation, iputs) \
if(!(operation)){ \
DPRINTF(("invalid operation in %s/%d\n", __FILE__, __LINE__));\
iputs; \
error = -EINVAL; \
goto do_return; \
}
#define RETAIN(inode) (inode)->i_count++
/* ------------------------------------------------------------------------- */
static int fo_new(int is_dir, int *fh, my_attr_t *fa, int dir,
char *name, my_attr_t *sa)
{
struct inode *inode, *new_inode = NULL;
struct inode_operations *ops;
int error = 0;
FIND_INODE(inode, dir, ops, ;);
VALIDATE(ops->lookup, ;);
CHECK_WRITE(inode, ;);
if(is_dir){
VALIDATE(ops->mkdir, ;);
RETAIN(inode); /* retain for mkdir */
error = ops->mkdir(inode, name, strlen(name), sa->mode);
if(error) goto do_return;
RETAIN(inode); /* retain for second operation */
error = ops->lookup(inode, name, strlen(name), &new_inode);
}else{
RETAIN(inode); /* retain for additional lookup or unlink */
if(sa->size == 0){
VALIDATE(ops->unlink, iput(inode));
ops->unlink(inode, name, strlen(name)); /* ignore errors */
}else{
if(ops->lookup(inode, name, strlen(name), &new_inode) == 0){
*fh = new_inode->i_ino;
get_attr(new_inode, fa);
iput(new_inode);
iput(inode);
return 0;
}
}
VALIDATE(ops->create, ;);
RETAIN(inode); /* retain for create */
error = ops->create(inode, name, strlen(name), sa->mode, &new_inode);
}
if(error)
goto do_return;
*fh = new_inode->i_ino;
new_inode->i_uid = nfs_uid;
new_inode->i_gid = nfs_gid;
new_inode->i_dirt = 1;
set_attr(new_inode, sa);
new_inode->i_mode &= ~S_IFMT;
new_inode->i_mode |= is_dir ? S_IFDIR : S_IFREG;
get_attr(new_inode, fa);
iput(new_inode);
do_return:
iput(inode);
return error;
}
/* ------------------------------------------------------------------------- */
int fo_create(int *fh, my_attr_t *fa, int dir, char *name, my_attr_t *sa)
{
DPRINTF(("fo_create(ino=%d, name=%s)\n", dir, name));
return fo_new(0, fh, fa, dir, name, sa);
}
/* ------------------------------------------------------------------------- */
int fo_mkdir(int *fh, my_attr_t *fa, int dir, char *name, my_attr_t *sa)
{
DPRINTF(("fo_mkdir(ino=%d, name=%s)\n", dir, name));
return fo_new(1, fh, fa, dir, name, sa);
}
/* ------------------------------------------------------------------------- */
int fo_getattr(my_attr_t *fa, int fh)
{
struct inode *inode;
struct inode_operations *ops;
int error = 0;
DPRINTF(("fo_getattr(ino=%d)\n", fh));
FIND_INODE(inode, fh, ops, ;);
get_attr(inode, fa);
iput(inode);
do_return:
return error;
}
/* ------------------------------------------------------------------------- */
int fo_lookup(int *fh, my_attr_t *fa, int dir, char *name)
{
struct inode *inode, *found_inode = NULL;
struct inode_operations *ops;
int error = 0;
DPRINTF(("fo_lookup(ino=%d, name=%s)\n", dir, name));
FIND_INODE(inode, dir, ops, ;);
CHECK_READ(inode, iput(inode));
VALIDATE(ops->lookup, iput(inode));
if((error = ops->lookup(inode, name, strlen(name), &found_inode)) != 0)
goto do_return;
get_attr(found_inode, fa);
*fh = found_inode->i_ino;
iput(found_inode);
do_return:
return error;
}
/* ------------------------------------------------------------------------- */
struct my_dirinfo{
my_direntry_t **entries;
int max_bytes;
int *put_cookie;
int called;
};
/* ------------------------------------------------------------------------- */
static int callback(void *arg, const char *name, int namelen,
off_t offs, ino_t inum)
{
struct my_dirinfo *di = arg;
my_direntry_t *p;
di->called = 1;
if(di->put_cookie != NULL)
*(di->put_cookie) = offs;
di->max_bytes -= namelen + 1 + sizeof(my_direntry_t);
if(di->max_bytes < 0){
DPRINTF(("callback(): stopping\n"));
return -1;
}
p = malloc(sizeof(my_direntry_t));
p->fh = inum;
p->name = malloc(namelen + 1);
memcpy(p->name, name, namelen);
p->name[namelen] = 0;
p->cookie = -1;
di->put_cookie = &p->cookie;
p->next = NULL;
*(di->entries) = p;
di->entries = &p->next;
DPRINTF(("callback(): added ->%s<- offset=%d, inode=%d\n",
p->name, (int)offs, (int)inum));
return 0;
}
/* ------------------------------------------------------------------------- */
int fo_readdir(my_direntry_t **result, int *eof, int max_bytes,
int dir, int cookie)
{
struct inode *inode;
struct inode_operations *ops;
struct file_operations *fops;
struct file filp;
int error = 0;
struct my_dirinfo dirinfo;
*result = NULL;
dirinfo.entries = result;
dirinfo.max_bytes = max_bytes;
dirinfo.put_cookie = NULL;
dirinfo.called = 0;
DPRINTF(("fo_readdir(ino=%d, cookie=%d, max=%d)\n",dir,cookie,max_bytes));
FIND_INODE(inode, dir, ops, ;);
CHECK_EXEC(inode, ;);
CHECK_READ(inode, ;);
VALIDATE(ops->default_file_ops, ;);
fops = ops->default_file_ops;
VALIDATE(fops->readdir, ;);
bzero(&filp, sizeof(filp));
filp.f_mode = inode->i_mode;
filp.f_pos = cookie;
do{
dirinfo.called = 0;
error = fops->readdir(inode, &filp, &dirinfo, callback);
if(error < 0)
break;
}while(dirinfo.max_bytes > 0 && dirinfo.called);
if(dirinfo.put_cookie != NULL)
*(dirinfo.put_cookie) = filp.f_pos;
do_return:
*eof = !dirinfo.called;
iput(inode);
return error;
}
/* ------------------------------------------------------------------------- */
int fo_setattr(my_attr_t *fa, int fh, my_attr_t *sa)
{
struct inode *inode;
struct inode_operations *ops;
int error = 0;
DPRINTF(("fo_setattr(ino=%d)\n", fh));
FIND_INODE(inode, fh, ops, ;);
CHECK_ATTR(inode, iput(inode));
set_attr(inode, sa);
get_attr(inode, fa);
iput(inode);
do_return:
return error;
}
/* ------------------------------------------------------------------------- */
static int fo_rm(int is_dir, int dir, char *name)
{
struct inode *inode;
struct inode_operations *ops;
int error = 0;
int (*rmop)(struct inode *,const char *,int);
DPRINTF(("fo_rm(ino=%d, name=%s)\n", dir, name));
FIND_INODE(inode, dir, ops, ;);
CHECK_WRITE(inode, iput(inode));
rmop = is_dir ? ops->rmdir : ops->unlink;
VALIDATE(rmop, iput(inode));
if((error = rmop(inode, name, strlen(name))) != 0)
goto do_return;
do_return:
return error;
}
/* ------------------------------------------------------------------------- */
int fo_remove(int dir, char *name)
{
return fo_rm(0, dir, name);
}
/* ------------------------------------------------------------------------- */
int fo_rmdir(int dir, char *name)
{
return fo_rm(1, dir, name);
}
/* ------------------------------------------------------------------------- */
int fo_rename(int fromdir, char *fromname, int todir, char *toname)
{
struct inode *from_i, *to_i;
struct inode_operations *from_o, *to_o;
int error = 0;
DPRINTF(("fo_rename(ino=%d, name=%s, ino=%d, name=%s)\n", fromdir,
fromname, todir, toname));
FIND_INODE(from_i, fromdir, from_o, ;);
CHECK_WRITE(from_i, iput(from_i));
FIND_INODE(to_i, todir, to_o, iput(from_i));
CHECK_WRITE(to_i, iput(from_i); iput(to_i));
VALIDATE(from_o->rename, iput(from_i); iput(to_i));
error = from_o->rename(from_i, fromname, strlen(fromname),
to_i, toname, strlen(toname), 0);
do_return:
return error;
}
/* ------------------------------------------------------------------------- */
int fo_statfs(my_statfs_t *fsstat)
{
struct statfs stat;
DPRINTF(("fo_statfs()\n"));
if(!my_superblock.s_op->statfs)
return -EINVAL;
my_superblock.s_op->statfs(&my_superblock, &stat, sizeof(stat));
fsstat->type = stat.f_type;
fsstat->bsize = stat.f_bsize;
fsstat->blocks = stat.f_blocks;
fsstat->bfree = stat.f_bfree;
fsstat->bavail = stat.f_bavail;
fsstat->files = stat.f_files;
fsstat->ffree = stat.f_ffree;
return 0;
}
/* ------------------------------------------------------------------------- */
int fo_read(my_attr_t *fa, int *len, char **data, int fh, int offs, int count)
{
static char transfer_buf[TRANSFER_BUFFER_SIZE];
struct inode *inode;
struct inode_operations *ops;
struct file_operations *fops;
struct file filp;
int error = 0;
DPRINTF(("fo_read(ino=%d, offs=%d, len=%d)\n", fh, offs, count));
*data = transfer_buf;
FIND_INODE(inode, fh, ops, ;);
CHECK_READ(inode, ;);
if(offs + count > inode->i_size)
count = inode->i_size - offs;
if(count <= 0){ /* nothing to be done */
get_attr(inode, fa);
*len = 0;
goto do_return;
}
VALIDATE(ops->default_file_ops, ;);
fops = ops->default_file_ops;
VALIDATE(fops->read, ;);
bzero(&filp, sizeof(filp));
filp.f_mode = inode->i_mode;
filp.f_pos = offs;
if(count > sizeof(transfer_buf))
count = sizeof(transfer_buf);
error = fops->read(inode, &filp, transfer_buf, count);
*len = error;
error = error < 0 ? error : 0;
get_attr(inode, fa);
do_return:
iput(inode);
DPRINTF(("fo_read() returns %d\n", error));
return error;
}
/* ------------------------------------------------------------------------- */
int fo_write(my_attr_t *fa, int fh, int offset, int count, char *data)
{
struct inode *inode;
struct inode_operations *ops;
struct file_operations *fops;
struct file filp;
int error = 0;
DPRINTF(("fo_write(ino=%d, offs=%d, len=%d)\n", fh, offset, count));
FIND_INODE(inode, fh, ops, ;);
CHECK_WRITE(inode, ;);
if(count <= 0){ /* nothing to be done */
get_attr(inode, fa);
goto do_return;
}
VALIDATE(ops->default_file_ops, ;);
fops = ops->default_file_ops;
VALIDATE(fops->write, ;);
bzero(&filp, sizeof(filp));
filp.f_mode = inode->i_mode;
filp.f_pos = offset;
error = fops->write(inode, &filp, data, count);
error = error < 0 ? error : 0;
get_attr(inode, fa);
do_return:
iput(inode);
DPRINTF(("fo_write() returns %d\n", error));
return error;
}
/* ------------------------------------------------------------------------- */
int fo_link(int from, int dir, char *name)
{
struct inode *from_i, *dir_i;
struct inode_operations *from_o, *dir_o;
int error = 0;
DPRINTF(("fo_link(ino=%d, dir=%d, name=%s)\n", from, dir, name));
FIND_INODE(from_i, from, from_o, ;);
FIND_INODE(dir_i, dir, dir_o, iput(from_i));
CHECK_WRITE(dir_i, iput(from_i); iput(dir_i));
VALIDATE(dir_o->link, iput(from_i); iput(dir_i));
error = dir_o->link(from_i, dir_i, name, strlen(name));
do_return:
return error;
}
/* ------------------------------------------------------------------------- */
int fo_readlink(char **path, int fh)
{
struct inode *inode;
struct inode_operations *ops;
int error = 0;
static char namebuf[2048];
DPRINTF(("fo_readlink(ino=%d)\n", fh));
FIND_INODE(inode, fh, ops, ;);
VALIDATE(ops->readlink, iput(inode));
*path = namebuf;
error = ops->readlink(inode, *path, sizeof(namebuf));
if(error >= 0){
if(error < sizeof(namebuf))
namebuf[error] = 0;
else
namebuf[sizeof(namebuf)-1] = 0;
}
do_return:
return error;
}
/* ------------------------------------------------------------------------- */
int fo_symlink(int fromdir, char *fromname, char *topath, my_attr_t *sa)
{
struct inode *dir_i;
struct inode_operations *dir_o;
int error = 0;
DPRINTF(("fo_symlink(dir=%d, name=%s, path=%s)\n", fromdir,
fromname, topath));
FIND_INODE(dir_i, fromdir, dir_o, ;);
CHECK_WRITE(dir_i, iput(dir_i));
VALIDATE(dir_o->symlink, iput(dir_i));
error = dir_o->symlink(dir_i, fromname, strlen(fromname), topath);
/* ignore attributes! links should have all flags set, anyway */
do_return:
return error;
}
/* ------------------------------------------------------------------------- */
void fops_regular(void)
{
static int i = 0;
if(++i > 10){ /* do a sync every 10s */
i = 0;
my_sync();
}
}
/* ------------------------------------------------------------------------- */
void fops_init(void)
{
#ifdef CONFIG_EXT_FS
init_ext_fs();
#endif
#ifdef CONFIG_EXT2_FS
init_ext2_fs();
#endif
#ifdef CONFIG_XIA_FS
init_xiafs_fs();
#endif
#ifdef CONFIG_MINIX_FS
init_minix_fs();
#endif
#ifdef CONFIG_UMSDOS_FS
init_umsdos_fs();
#endif
#ifdef CONFIG_FAT_FS
init_fat_fs();
#endif
#ifdef CONFIG_MSDOS_FS
init_msdos_fs();
#endif
#ifdef CONFIG_VFAT_FS
init_vfat_fs();
#endif
#ifdef CONFIG_PROC_FS
init_proc_fs();
#endif
#ifdef CONFIG_NFS_FS
init_nfs_fs();
#endif
#ifdef CONFIG_SMB_FS
init_smb_fs();
#endif
#ifdef CONFIG_NCP_FS
init_ncp_fs();
#endif
#ifdef CONFIG_ISO9660_FS
init_iso9660_fs();
#endif
#ifdef CONFIG_SYSV_FS
init_sysv_fs();
#endif
#ifdef CONFIG_HPFS_FS
init_hpfs_fs();
#endif
#ifdef CONFIG_AFFS_FS
init_affs_fs();
#endif
#ifdef CONFIG_UFS_FS
init_ufs_fs();
#endif
#ifdef CONFIG_NTFS_FS
init_ntfs_fs();
#endif
}
/* ------------------------------------------------------------------------- */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.