Commit 67bb51b9 authored by David Howells's avatar David Howells Committed by Linus Torvalds

[PATCH] AFS filesystem 2/2

Here's a patch to add an Andrew File System (AFS) driver to the kernel.
Currently it only provides read-only, uncached, non-automounted and
unsecured support.
parent 3d970ece
kAFS: AFS FILESYSTEM
====================
ABOUT
=====
This filesystem provides a fairly simple AFS filesystem driver. It is under
development and only provides very basic facilities. It does not yet support
the following AFS features:
(*) Write support.
(*) Communications security.
(*) Local caching.
(*) pioctl() system call.
(*) Automatic mounting of embedded mountpoints.
USAGE
=====
When inserting the driver modules the root cell must be specified along with a
list of volume location server IP addresses:
insmod rxrpc.o
insmod kafs.o rootcell=cambridge.redhat.com:172.16.18.73:172.16.18.91
The first module is a driver for the RxRPC remote operation protocol, and the
second is the actual filesystem driver for the AFS filesystem.
Once the module has been loaded, more modules can be added by the following
procedure:
echo add grand.central.org 18.7.14.88:128.2.191.224 >/proc/fs/afs/cells
Where the parameters to the "add" command are the name of a cell and a list of
volume location servers within that cell.
Filesystems can be mounted anywhere by commands similar to the following:
mount -t afs "%cambridge.redhat.com:root.afs." /afs
mount -t afs "#cambridge.redhat.com:root.cell." /afs/cambridge
mount -t afs "#root.afs." /afs
mount -t afs "#root.cell." /afs/cambridge
NB: When using this on Linux 2.4, the mount command has to be different,
since the filesystem doesn't have access to the device name argument:
mount -t afs none /afs -ovol="#root.afs."
Where the initial character is either a hash or a percent symbol depending on
whether you definitely want a R/W volume (hash) or whether you'd prefer a R/O
volume, but are willing to use a R/W volume instead (percent).
The name of the volume can be suffixes with ".backup" or ".readonly" to
specify connection to only volumes of those types.
The name of the cell is optional, and if not given during a mount, then the
named volume will be looked up in the cell specified during insmod.
Additional cells can be added through /proc (see later section).
MOUNTPOINTS
===========
AFS has a concept of mountpoints. These are specially formatted symbolic links
(of the same form as the "device name" passed to mount). kAFS presents these
to the user as directories that have special properties:
(*) They cannot be listed. Running a program like "ls" on them will incur an
EREMOTE error (Object is remote).
(*) Other objects can't be looked up inside of them. This also incurs an
EREMOTE error.
(*) They can be queried with the readlink() system call, which will return
the name of the mountpoint to which they point. The "readlink" program
will also work.
(*) They can be mounted on (which symbolic links can't).
PROC FILESYSTEM
===============
The rxrpc module creates a number of files in various places in the /proc
filesystem:
(*) Firstly, some information files are made available in a directory called
"/proc/net/rxrpc/". These list the extant transport endpoint, peer,
connection and call records.
(*) Secondly, some control files are made available in a directory called
"/proc/sys/rxrpc/". Currently, all these files can be used for is to
turn on various levels of tracing.
The AFS modules creates a "/proc/fs/afs/" directory and populates it:
(*) A "cells" file that lists cells currently known to the afs module.
(*) A directory per cell that contains files that list volume location
servers, volumes, and active servers known within that cell.
THE CELL DATABASE
=================
The filesystem maintains an internal database of all the cells it knows and
the IP addresses of the volume location servers for those cells. The cell to
which the computer belongs is added to the database when insmod is performed
by the "rootcell=" argument.
Further cells can be added by commands similar to the following:
echo add CELLNAME VLADDR[:VLADDR][:VLADDR]... >/proc/fs/afs/cells
echo add grand.central.org 18.7.14.88:128.2.191.224 >/proc/fs/afs/cells
No other cell database operations are available at this time.
EXAMPLES
========
Here's what I use to test this. Some of the names and IP addresses are local
to my internal DNS. My "root.afs" partition has a mount point within it for
some public volumes volumes.
insmod -S /tmp/rxrpc.o
insmod -S /tmp/kafs.o rootcell=cambridge.redhat.com:172.16.18.73:172.16.18.91
mount -t afs \%root.afs. /afs
mount -t afs \%cambridge.redhat.com:root.cell. /afs/cambridge.redhat.com/
echo add grand.central.org 18.7.14.88:128.2.191.224 > /proc/fs/afs/cells
mount -t afs "#grand.central.org:root.cell." /afs/grand.central.org/
mount -t afs "#grand.central.org:root.archive." /afs/grand.central.org/archive
mount -t afs "#grand.central.org:root.contrib." /afs/grand.central.org/contrib
mount -t afs "#grand.central.org:root.doc." /afs/grand.central.org/doc
mount -t afs "#grand.central.org:root.project." /afs/grand.central.org/project
mount -t afs "#grand.central.org:root.service." /afs/grand.central.org/service
mount -t afs "#grand.central.org:root.software." /afs/grand.central.org/software
mount -t afs "#grand.central.org:root.user." /afs/grand.central.org/user
umount /afs/grand.central.org/user
umount /afs/grand.central.org/software
umount /afs/grand.central.org/service
umount /afs/grand.central.org/project
umount /afs/grand.central.org/doc
umount /afs/grand.central.org/contrib
umount /afs/grand.central.org/archive
umount /afs/grand.central.org
umount /afs/cambridge.redhat.com
umount /afs
rmmod kafs
rmmod rxrpc
......@@ -1144,3 +1144,11 @@ CONFIG_XFS_RT
If unsure, say N.
CONFIG_AFS_FS
If you say Y here, you will get an experimental Andrew File System
driver. It currently only supports unsecured read-only AFS access.
See Documentation/filesystems/afs.txt for more intormation.
If unsure, say N.
......@@ -158,6 +158,16 @@ if [ "$CONFIG_NET" = "y" ]; then
# for fs/nls/Config.in
define_bool CONFIG_NCPFS_NLS n
fi
dep_tristate 'Andrew File System support (AFS) (Experimental)' CONFIG_AFS_FS $CONFIG_INET $CONFIG_EXPERIMENTAL
if [ "$CONFIG_AFS_FS" = "y" ]; then
define_tristate CONFIG_RXRPC y
else
if [ "$CONFIG_AFS_FS" = "m" ]; then
define_tristate CONFIG_RXRPC m
fi
fi
endmenu
else
......
......@@ -86,5 +86,6 @@ obj-$(CONFIG_REISERFS_FS) += reiserfs/
obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/
obj-$(CONFIG_JFS_FS) += jfs/
obj-$(CONFIG_XFS_FS) += xfs/
obj-$(CONFIG_AFS_FS) += afs/
include $(TOPDIR)/Rules.make
#
# Makefile for Red Hat Linux AFS client.
#
kafs-objs := \
callback.o \
cell.o \
cmservice.o \
dir.o \
file.o \
fsclient.o \
inode.o \
kafsasyncd.o \
kafstimod.o \
main.o \
misc.o \
mntpt.o \
proc.o \
server.o \
super.o \
vlclient.o \
vlocation.o \
vnode.o \
volume.o
# cache.o
obj-m := kafs.o
# superfluous for 2.5, but needed for 2.4..
ifeq "$(VERSION).$(PATCHLEVEL)" "2.4"
kafs.o: $(kafs-objs)
$(LD) -r -o kafs.o $(kafs-objs)
endif
include $(TOPDIR)/Rules.make
/* cache-layout.h: AFS cache layout
*
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*
* The cache is stored on a block device and is laid out as:
*
* 0 +------------------------------------------------
* |
* | SuperBlock
* |
* 1 +------------------------------------------------
* |
* | file-meta-data File: Data block #0
* | - file-meta-data file (volix #0 file #0) : Meta-data block
* | - contains direct pointers to first 64 file data blocks
* | - Cached cell catalogue file (volix #0 file #1) file: Meta-data block
* | - Cached volume location catalogue file (volix #0 file #2): Meta-data block
* | - Vnode catalogue hash bucket #n file: Meta-data block
* |
* 2 +------------------------------------------------
* |
* | Bitmap Block Allocation Bitmap
* | - 1 bit per block in the bitmap block
* | - bit 0 of dword 0 refers to the bitmap block 0
* | - set if the bitmap block is full
* | - 32768 bits per block, requiring 4 blocks for a 16Tb cache
* | - bitmap bitmap blocks are cleared initially
* | - not present if <4 bitmap blocks
* |
* +------------------------------------------------
* |
* | File Block Allocation Bitmap
* | - 1 bit per block in the cache
* | - bit 0 of dword 0 refers to the first block of the data cache
* | - set if block is allocated
* | - 32768 bits per block, requiring 131072 blocks for a 16Tb cache
* | - bitmap blocks are cleared lazily (sb->bix_bitmap_unready)
* |
* +------------------------------------------------
* |
* | Data Cache
* |
* End +------------------------------------------------
*
* Blocks are indexed by an unsigned 32-bit word, meaning that the cache can hold up to 2^32 pages,
* or 16Tb in total.
*
* Credentials will be cached in memory, since they are subject to change without notice, and are
* difficult to derive manually, being constructed from the following information:
* - per vnode user ID and mode mask
* - parent directory ACL
* - directory ACL (dirs only)
* - group lists from ptserver
*/
#ifndef _LINUX_AFS_CACHE_LAYOUT_H
#define _LINUX_AFS_CACHE_LAYOUT_H
#include "types.h"
typedef u32 afsc_blockix_t;
typedef u32 afsc_cellix_t;
/* Cached volume index
* - afsc_volix_t/4 is the index into the volume cache
* - afsc_volix_t%4 is 0 for R/W, 1 for R/O and 2 for Bak (3 is not used)
* - afsc_volix_t==0-3 refers to a "virtual" volume that stores meta-data about the cache
*/
typedef struct {
u32 index;
} afsc_volix_t;
#define AFSC_VNCAT_HASH_NBUCKETS 128
/* special meta file IDs (all cell 0 vol 0) */
enum afsc_meta_fids {
AFSC_META_FID_METADATA = 0,
AFSC_META_FID_CELL_CATALOGUE = 1,
AFSC_META_FID_VLDB_CATALOGUE = 2,
AFSC_META_FID_VNODE_CATALOGUE0 = 3,
AFSC_META_FID__COUNT = AFSC_VNCAT_HASH_NBUCKETS + 3
};
/*****************************************************************************/
/*
* cache superblock block layout
* - the blockdev is prepared for initialisation by 'echo "kafsuninit" >/dev/hdaXX' before mounting
* - when initialised, the magic number is changed to "kafs-cache"
*/
struct afsc_super_block
{
char magic[10]; /* magic number */
#define AFSC_SUPER_MAGIC "kafs-cache"
#define AFSC_SUPER_MAGIC_NEEDS_INIT "kafsuninit"
#define AFSC_SUPER_MAGIC_SIZE 10
unsigned short endian; /* 0x1234 stored CPU-normal order */
#define AFSC_SUPER_ENDIAN 0x1234
unsigned version; /* format version */
#define AFSC_SUPER_VERSION 1
/* layout */
unsigned bsize; /* cache block size */
afsc_blockix_t bix_bitmap_fullmap; /* block ix of bitmap full bitmap */
afsc_blockix_t bix_bitmap; /* block ix of alloc bitmap */
afsc_blockix_t bix_bitmap_unready; /* block ix of unready area of bitmap */
afsc_blockix_t bix_cache; /* block ix of data cache */
afsc_blockix_t bix_end; /* block ix of end of cache */
};
/*****************************************************************************/
/*
* vnode (inode) metadata cache record
* - padded out to 512 bytes and stored eight to a page
* - only the data version is necessary
* - disconnected operation is not supported
* - afs_iget() contacts the server to get the meta-data _anyway_ when an inode is first brought
* into memory
* - at least 64 direct block pointers will be available (a directory is max 256Kb)
* - any block pointer which is 0 indicates an uncached page
*/
struct afsc_vnode_meta
{
/* file ID */
afsc_volix_t volume_ix; /* volume catalogue index */
unsigned vnode; /* vnode number */
unsigned unique; /* FID unique */
unsigned size; /* size of file */
time_t mtime; /* last modification time */
/* file status */
afs_dataversion_t version; /* current data version */
/* file contents */
afsc_blockix_t dbl_indirect; /* double indirect block index */
afsc_blockix_t indirect; /* single indirect block 0 index */
afsc_blockix_t direct[0]; /* direct block index (#AFSC_VNODE_META_DIRECT) */
};
#define AFSC_VNODE_META_RECSIZE 512 /* record size */
#define AFSC_VNODE_META_DIRECT \
((AFSC_VNODE_META_RECSIZE-sizeof(struct afsc_vnode_meta))/sizeof(afsc_blockix_t))
#define AFSC_VNODE_META_PER_PAGE (PAGE_SIZE / AFSC_VNODE_META_RECSIZE)
/*****************************************************************************/
/*
* entry in the cached cell catalogue
*/
struct afsc_cell_record
{
char name[64]; /* cell name (padded with NULs) */
struct in_addr servers[16]; /* cached cell servers */
};
/*****************************************************************************/
/*
* entry in the cached volume location catalogue
* - indexed by afsc_volix_t/4
*/
struct afsc_vldb_record
{
char name[64]; /* volume name (padded with NULs) */
afs_volid_t vid[3]; /* volume IDs for R/W, R/O and Bak volumes */
unsigned char vidmask; /* voltype mask for vid[] */
unsigned char _pad[1];
unsigned short nservers; /* number of entries used in servers[] */
struct in_addr servers[8]; /* fileserver addresses */
unsigned char srvtmask[8]; /* voltype masks for servers[] */
#define AFSC_VOL_STM_RW 0x01 /* server holds a R/W version of the volume */
#define AFSC_VOL_STM_RO 0x02 /* server holds a R/O version of the volume */
#define AFSC_VOL_STM_BAK 0x04 /* server holds a backup version of the volume */
afsc_cellix_t cell_ix; /* cell catalogue index (MAX_UINT if unused) */
time_t ctime; /* time at which cached */
};
/*****************************************************************************/
/*
* vnode catalogue entry
* - must be 2^x size so that do_generic_file_read doesn't present them split across pages
*/
struct afsc_vnode_catalogue
{
afsc_volix_t volume_ix; /* volume catalogue index */
afs_vnodeid_t vnode; /* vnode ID */
u32 meta_ix; /* metadata file index */
u32 atime; /* last time entry accessed */
} __attribute__((packed));
#define AFSC_VNODE_CATALOGUE_PER_BLOCK ((size_t)(PAGE_SIZE/sizeof(struct afsc_vnode_catalogue)))
/*****************************************************************************/
/*
* vnode data "page directory" block
* - first 1024 pages don't map through here
* - PAGE_SIZE in size
*/
struct afsc_indirect_block
{
afsc_blockix_t pt_bix[1024]; /* "page table" block indices */
};
/*****************************************************************************/
/*
* vnode data "page table" block
* - PAGE_SIZE in size
*/
struct afsc_dbl_indirect_block
{
afsc_blockix_t page_bix[1024]; /* "page" block indices */
};
#endif /* _LINUX_AFS_CACHE_LAYOUT_H */
/*
* Copyright (c) 2002 Red Hat, Inc. All rights reserved.
*
* This software may be freely redistributed under the terms of the
* GNU General Public License.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Authors: David Woodhouse <dwmw2@cambridge.redhat.com>
* David Howells <dhowells@redhat.com>
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include "server.h"
#include "vnode.h"
#include "internal.h"
/*****************************************************************************/
/*
* allow the fileserver to request callback state (re-)initialisation
*/
int SRXAFSCM_InitCallBackState(afs_server_t *server)
{
struct list_head callbacks;
_enter("%p",server);
INIT_LIST_HEAD(&callbacks);
/* transfer the callback list from the server to a temp holding area */
spin_lock(&server->cb_lock);
list_add(&callbacks,&server->cb_promises);
list_del_init(&server->cb_promises);
/* munch our way through the list, grabbing the inode, dropping all the locks and regetting
* them in the right order
*/
while (!list_empty(&callbacks)) {
struct inode *inode;
afs_vnode_t *vnode;
vnode = list_entry(callbacks.next,afs_vnode_t,cb_link);
list_del_init(&vnode->cb_link);
/* try and grab the inode - may fail */
inode = igrab(AFS_VNODE_TO_I(vnode));
if (inode) {
int release = 0;
spin_unlock(&server->cb_lock);
spin_lock(&vnode->lock);
if (vnode->cb_server==server) {
vnode->cb_server = NULL;
afs_kafstimod_del_timer(&vnode->cb_timeout);
spin_lock(&afs_cb_hash_lock);
list_del_init(&vnode->cb_hash_link);
spin_unlock(&afs_cb_hash_lock);
release = 1;
}
spin_unlock(&vnode->lock);
iput(inode);
if (release) afs_put_server(server);
spin_lock(&server->cb_lock);
}
}
spin_unlock(&server->cb_lock);
_leave(" = 0");
return 0;
} /* end SRXAFSCM_InitCallBackState() */
/*****************************************************************************/
/*
* allow the fileserver to break callback promises
*/
int SRXAFSCM_CallBack(afs_server_t *server, size_t count, afs_callback_t callbacks[])
{
struct list_head *_p;
_enter("%p,%u,",server,count);
for (; count>0; callbacks++, count--) {
struct inode *inode = NULL;
afs_vnode_t *vnode = NULL;
int valid = 0;
_debug("- Fid { vl=%08x n=%u u=%u } CB { v=%u x=%u t=%u }",
callbacks->fid.vid,
callbacks->fid.vnode,
callbacks->fid.unique,
callbacks->version,
callbacks->expiry,
callbacks->type
);
/* find the inode for this fid */
spin_lock(&afs_cb_hash_lock);
list_for_each(_p,&afs_cb_hash(server,&callbacks->fid)) {
vnode = list_entry(_p,afs_vnode_t,cb_hash_link);
if (memcmp(&vnode->fid,&callbacks->fid,sizeof(afs_fid_t))!=0)
continue;
/* right vnode, but is it same server? */
if (vnode->cb_server!=server)
break; /* no */
/* try and nail the inode down */
inode = igrab(AFS_VNODE_TO_I(vnode));
break;
}
spin_unlock(&afs_cb_hash_lock);
if (inode) {
/* we've found the record for this vnode */
spin_lock(&vnode->lock);
if (vnode->cb_server==server) {
/* the callback _is_ on the calling server */
vnode->cb_server = NULL;
valid = 1;
afs_kafstimod_del_timer(&vnode->cb_timeout);
vnode->flags |= AFS_VNODE_CHANGED;
spin_lock(&server->cb_lock);
list_del_init(&vnode->cb_link);
spin_unlock(&server->cb_lock);
spin_lock(&afs_cb_hash_lock);
list_del_init(&vnode->cb_hash_link);
spin_unlock(&afs_cb_hash_lock);
}
spin_unlock(&vnode->lock);
if (valid) {
invalidate_inode_pages(inode->i_mapping);
afs_put_server(server);
}
iput(inode);
}
}
_leave(" = 0");
return 0;
} /* end SRXAFSCM_CallBack() */
/*****************************************************************************/
/*
* allow the fileserver to see if the cache manager is still alive
*/
int SRXAFSCM_Probe(afs_server_t *server)
{
_debug("SRXAFSCM_Probe(%p)\n",server);
return 0;
} /* end SRXAFSCM_Probe() */
This diff is collapsed.
/* cell.h: AFS cell record
*
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef _LINUX_AFS_CELL_H
#define _LINUX_AFS_CELL_H
#include "types.h"
extern volatile int afs_cells_being_purged; /* T when cells are being purged by rmmod */
/*****************************************************************************/
/*
* AFS cell record
*/
struct afs_cell
{
atomic_t usage;
struct list_head link; /* main cell list link */
struct list_head proc_link; /* /proc cell list link */
struct proc_dir_entry *proc_dir; /* /proc dir for this cell */
struct list_head caches; /* list of caches currently backing this cell */
/* server record management */
rwlock_t sv_lock; /* active server list lock */
struct list_head sv_list; /* active server list */
struct list_head sv_graveyard; /* inactive server list */
spinlock_t sv_gylock; /* inactive server list lock */
/* volume location record management */
struct rw_semaphore vl_sem; /* volume management serialisation semaphore */
struct list_head vl_list; /* cell's active VL record list */
struct list_head vl_graveyard; /* cell's inactive VL record list */
spinlock_t vl_gylock; /* graveyard lock */
unsigned short vl_naddrs; /* number of VL servers in addr list */
unsigned short vl_curr_svix; /* current server index */
struct in_addr vl_addrs[16]; /* cell VL server addresses */
char name[0]; /* cell name - must go last */
};
extern int afs_cell_init(void);
extern int afs_cell_create(const char *name, char *vllist, afs_cell_t **_cell);
extern int afs_cell_lookup(const char *name, afs_cell_t **_cell);
#define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0)
extern afs_cell_t *afs_get_cell_maybe(afs_cell_t **_cell);
extern void afs_put_cell(afs_cell_t *cell);
extern void afs_cell_purge(void);
#endif /* _LINUX_AFS_CELL_H */
This diff is collapsed.
/* cmservice.h: AFS Cache Manager Service declarations
*
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef _LINUX_AFS_CMSERVICE_H
#define _LINUX_AFS_CMSERVICE_H
#include <rxrpc/transport.h>
#include "types.h"
/* cache manager start/stop */
extern int afscm_start(void);
extern void afscm_stop(void);
/* cache manager server functions */
extern int SRXAFSCM_InitCallBackState(afs_server_t *server);
extern int SRXAFSCM_CallBack(afs_server_t *server, size_t count, afs_callback_t callbacks[]);
extern int SRXAFSCM_Probe(afs_server_t *server);
#endif /* _LINUX_AFS_CMSERVICE_H */
This diff is collapsed.
/* errors.h: AFS abort/error codes
*
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef _H_DB712916_5113_11D6_9A6D_0002B3163499
#define _H_DB712916_5113_11D6_9A6D_0002B3163499
#include "types.h"
/* file server abort codes */
typedef enum {
VSALVAGE = 101, /* volume needs salvaging */
VNOVNODE = 102, /* no such file/dir (vnode) */
VNOVOL = 103, /* no such volume or volume unavailable */
VVOLEXISTS = 104, /* volume name already exists */
VNOSERVICE = 105, /* volume not currently in service */
VOFFLINE = 106, /* volume is currently offline (more info available [VVL-spec]) */
VONLINE = 107, /* volume is already online */
VDISKFULL = 108, /* disk partition is full */
VOVERQUOTA = 109, /* volume's maximum quota exceeded */
VBUSY = 110, /* volume is temporarily unavailable */
VMOVED = 111, /* volume moved to new server - ask this FS where */
} afs_rxfs_abort_t;
extern int afs_abort_to_error(int abortcode);
#endif /* _H_DB712916_5113_11D6_9A6D_0002B3163499 */
/* file.c: AFS filesystem file handling
*
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/pagemap.h>
#include "volume.h"
#include "vnode.h"
#include <rxrpc/call.h>
#include "internal.h"
//static int afs_file_open(struct inode *inode, struct file *file);
//static int afs_file_release(struct inode *inode, struct file *file);
static int afs_file_readpage(struct file *file, struct page *page);
//static ssize_t afs_file_read(struct file *file, char *buf, size_t size, loff_t *off);
static ssize_t afs_file_write(struct file *file, const char *buf, size_t size, loff_t *off);
struct inode_operations afs_file_inode_operations = {
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
.getattr = afs_inode_getattr,
#else
.revalidate = afs_inode_revalidate,
#endif
};
struct file_operations afs_file_file_operations = {
// .open = afs_file_open,
// .release = afs_file_release,
.read = generic_file_read, //afs_file_read,
.write = afs_file_write,
.mmap = generic_file_mmap,
// .fsync = afs_file_fsync,
};
struct address_space_operations afs_fs_aops = {
.readpage = afs_file_readpage,
};
/*****************************************************************************/
/*
* AFS file read
*/
#if 0
static ssize_t afs_file_read(struct file *file, char *buf, size_t size, loff_t *off)
{
struct afs_inode_info *ai;
ai = AFS_FS_I(file->f_dentry->d_inode);
if (ai->flags & AFS_INODE_DELETED)
return -ESTALE;
return -EIO;
} /* end afs_file_read() */
#endif
/*****************************************************************************/
/*
* AFS file write
*/
static ssize_t afs_file_write(struct file *file, const char *buf, size_t size, loff_t *off)
{
afs_vnode_t *vnode;
vnode = AFS_FS_I(file->f_dentry->d_inode);
if (vnode->flags & AFS_VNODE_DELETED)
return -ESTALE;
return -EIO;
} /* end afs_file_write() */
/*****************************************************************************/
/*
* AFS read page from file (or symlink)
*/
static int afs_file_readpage(struct file *file, struct page *page)
{
struct afs_rxfs_fetch_descriptor desc;
struct inode *inode;
afs_vnode_t *vnode;
int ret;
inode = page->mapping->host;
_enter("{%lu},{%lu}",inode->i_ino,page->index);
vnode = AFS_FS_I(inode);
if (!PageLocked(page))
PAGE_BUG(page);
ret = -ESTALE;
if (vnode->flags & AFS_VNODE_DELETED)
goto error;
/* work out how much to get and from where */
desc.fid = vnode->fid;
desc.offset = page->index << PAGE_CACHE_SHIFT;
desc.size = min((size_t)(inode->i_size - desc.offset),(size_t)PAGE_SIZE);
desc.buffer = kmap(page);
clear_page(desc.buffer);
/* read the contents of the file from the server into the page */
ret = afs_vnode_fetch_data(vnode,&desc);
kunmap(page);
if (ret<0) {
if (ret==-ENOENT) {
_debug("got NOENT from server - marking file deleted and stale");
vnode->flags |= AFS_VNODE_DELETED;
ret = -ESTALE;
}
goto error;
}
SetPageUptodate(page);
unlock_page(page);
_leave(" = 0");
return 0;
error:
SetPageError(page);
unlock_page(page);
_leave(" = %d",ret);
return ret;
} /* end afs_file_readpage() */
This diff is collapsed.
/* fsclient.h: AFS File Server client stub declarations
*
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef _LINUX_AFS_FSCLIENT_H
#define _LINUX_AFS_FSCLIENT_H
#include "server.h"
extern int afs_rxfs_get_volume_info(afs_server_t *server,
const char *name,
afs_volume_info_t *vinfo);
extern int afs_rxfs_fetch_file_status(afs_server_t *server,
afs_vnode_t *vnode,
afs_volsync_t *volsync);
struct afs_rxfs_fetch_descriptor {
afs_fid_t fid; /* file ID to fetch */
size_t size; /* total number of bytes to fetch */
off_t offset; /* offset in file to start from */
void *buffer; /* read buffer */
size_t actual; /* actual size sent back by server */
};
extern int afs_rxfs_fetch_file_data(afs_server_t *server,
afs_vnode_t *vnode,
struct afs_rxfs_fetch_descriptor *desc,
afs_volsync_t *volsync);
extern int afs_rxfs_give_up_callback(afs_server_t *server, afs_vnode_t *vnode);
/* this doesn't appear to work in OpenAFS server */
extern int afs_rxfs_lookup(afs_server_t *server,
afs_vnode_t *dir,
const char *filename,
afs_vnode_t *vnode,
afs_volsync_t *volsync);
/* this is apparently mis-implemented in OpenAFS server */
extern int afs_rxfs_get_root_volume(afs_server_t *server,
char *buf,
size_t *buflen);
#endif /* _LINUX_AFS_FSCLIENT_H */
This diff is collapsed.
/* internal.h: internal AFS stuff
*
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef AFS_INTERNAL_H
#define AFS_INTERNAL_H
#include <linux/version.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/pagemap.h>
/*
* debug tracing
*/
#define kenter(FMT,...) printk("==> %s("FMT")\n",__FUNCTION__,##__VA_ARGS__)
#define kleave(FMT,...) printk("<== %s()"FMT"\n",__FUNCTION__,##__VA_ARGS__)
#define kdebug(FMT,...) printk(FMT"\n",##__VA_ARGS__)
#define kproto(FMT,...) printk("### "FMT"\n",##__VA_ARGS__)
#define knet(FMT,...) printk(FMT"\n",##__VA_ARGS__)
#if 0
#define _enter(FMT,...) kenter(FMT,##__VA_ARGS__)
#define _leave(FMT,...) kleave(FMT,##__VA_ARGS__)
#define _debug(FMT,...) kdebug(FMT,##__VA_ARGS__)
#define _proto(FMT,...) kproto(FMT,##__VA_ARGS__)
#define _net(FMT,...) knet(FMT,##__VA_ARGS__)
#else
#define _enter(FMT,...) do { } while(0)
#define _leave(FMT,...) do { } while(0)
#define _debug(FMT,...) do { } while(0)
#define _proto(FMT,...) do { } while(0)
#define _net(FMT,...) do { } while(0)
#endif
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
#define wait_on_page_locked wait_on_page
#define PageUptodate Page_Uptodate
static inline struct proc_dir_entry *PDE(const struct inode *inode)
{
return (struct proc_dir_entry *)inode->u.generic_ip;
}
#endif
static inline void afs_discard_my_signals(void)
{
while (signal_pending(current)) {
siginfo_t sinfo;
spin_lock_irq(&current->sig->siglock);
dequeue_signal(&current->blocked,&sinfo);
spin_unlock_irq(&current->sig->siglock);
}
}
/*
* cell.c
*/
extern struct rw_semaphore afs_proc_cells_sem;
extern struct list_head afs_proc_cells;
/*
* dir.c
*/
extern struct inode_operations afs_dir_inode_operations;
extern struct file_operations afs_dir_file_operations;
/*
* file.c
*/
extern struct address_space_operations afs_fs_aops;
extern struct inode_operations afs_file_inode_operations;
extern struct file_operations afs_file_file_operations;
/*
* inode.c
*/
extern int afs_iget(struct super_block *sb, afs_fid_t *fid, struct inode **_inode);
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
extern int afs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat);
#else
extern void afs_read_inode2(struct inode *inode, void *opaque);
extern int afs_inode_revalidate(struct dentry *dentry);
#endif
extern void afs_clear_inode(struct inode *inode);
/*
* mntpt.c
*/
extern struct inode_operations afs_mntpt_inode_operations;
extern struct file_operations afs_mntpt_file_operations;
extern int afs_mntpt_check_symlink(afs_vnode_t *vnode);
/*
* super.c
*/
extern int afs_fs_init(void);
extern void afs_fs_exit(void);
#define AFS_CB_HASH_COUNT (PAGE_SIZE/sizeof(struct list_head))
extern struct list_head afs_cb_hash_tbl[];
extern spinlock_t afs_cb_hash_lock;
#define afs_cb_hash(SRV,FID) \
afs_cb_hash_tbl[((unsigned)(SRV) + (FID)->vid + (FID)->vnode + (FID)->unique) % \
AFS_CB_HASH_COUNT]
/*
* proc.c
*/
extern int afs_proc_init(void);
extern void afs_proc_cleanup(void);
extern int afs_proc_cell_setup(afs_cell_t *cell);
extern void afs_proc_cell_remove(afs_cell_t *cell);
#endif /* AFS_INTERNAL_H */
/* kafsasyncd.c: AFS asynchronous operation daemon
*
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*
* The AFS async daemon is used to the following:
* - probe "dead" servers to see whether they've come back to life yet.
* - probe "live" servers that we haven't talked to for a while to see if they are better
* candidates for serving than what we're currently using
* - poll volume location servers to keep up to date volume location lists
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/completion.h>
#include "cell.h"
#include "server.h"
#include "volume.h"
#include "kafsasyncd.h"
#include "kafstimod.h"
#include <rxrpc/call.h>
#include <asm/errno.h>
#include "internal.h"
static DECLARE_COMPLETION(kafsasyncd_alive);
static DECLARE_COMPLETION(kafsasyncd_dead);
static DECLARE_WAIT_QUEUE_HEAD(kafsasyncd_sleepq);
static struct task_struct *kafsasyncd_task;
static int kafsasyncd_die;
static int kafsasyncd(void *arg);
static LIST_HEAD(kafsasyncd_async_attnq);
static LIST_HEAD(kafsasyncd_async_busyq);
static spinlock_t kafsasyncd_async_lock = SPIN_LOCK_UNLOCKED;
static void kafsasyncd_null_call_attn_func(struct rxrpc_call *call)
{
}
static void kafsasyncd_null_call_error_func(struct rxrpc_call *call)
{
}
/*****************************************************************************/
/*
* start the async daemon
*/
int afs_kafsasyncd_start(void)
{
int ret;
ret = kernel_thread(kafsasyncd,NULL,0);
if (ret<0)
return ret;
wait_for_completion(&kafsasyncd_alive);
return ret;
} /* end afs_kafsasyncd_start() */
/*****************************************************************************/
/*
* stop the async daemon
*/
void afs_kafsasyncd_stop(void)
{
/* get rid of my daemon */
kafsasyncd_die = 1;
wake_up(&kafsasyncd_sleepq);
wait_for_completion(&kafsasyncd_dead);
} /* end afs_kafsasyncd_stop() */
/*****************************************************************************/
/*
* probing daemon
*/
static int kafsasyncd(void *arg)
{
DECLARE_WAITQUEUE(myself,current);
struct list_head *_p;
int die;
kafsasyncd_task = current;
printk("kAFS: Started kafsasyncd %d\n",current->pid);
strcpy(current->comm,"kafsasyncd");
daemonize();
complete(&kafsasyncd_alive);
/* only certain signals are of interest */
spin_lock_irq(&current->sig->siglock);
siginitsetinv(&current->blocked,0);
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,3)
recalc_sigpending();
#else
recalc_sigpending(current);
#endif
spin_unlock_irq(&current->sig->siglock);
/* loop around looking for things to attend to */
do {
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&kafsasyncd_sleepq,&myself);
for (;;) {
if (!list_empty(&kafsasyncd_async_attnq) ||
signal_pending(current) ||
kafsasyncd_die)
break;
schedule();
set_current_state(TASK_INTERRUPTIBLE);
}
remove_wait_queue(&kafsasyncd_sleepq,&myself);
set_current_state(TASK_RUNNING);
/* discard pending signals */
afs_discard_my_signals();
die = kafsasyncd_die;
/* deal with the next asynchronous operation requiring attention */
if (!list_empty(&kafsasyncd_async_attnq)) {
struct afs_async_op *op;
_debug("@@@ Begin Asynchronous Operation");
op = NULL;
spin_lock(&kafsasyncd_async_lock);
if (!list_empty(&kafsasyncd_async_attnq)) {
op = list_entry(kafsasyncd_async_attnq.next,afs_async_op_t,link);
list_del(&op->link);
list_add_tail(&op->link,&kafsasyncd_async_busyq);
}
spin_unlock(&kafsasyncd_async_lock);
_debug("@@@ Operation %p {%p}\n",op,op?op->ops:NULL);
if (op)
op->ops->attend(op);
_debug("@@@ End Asynchronous Operation");
}
} while(!die);
/* need to kill all outstanding asynchronous operations before exiting */
kafsasyncd_task = NULL;
spin_lock(&kafsasyncd_async_lock);
/* fold the busy and attention queues together */
list_splice(&kafsasyncd_async_busyq,&kafsasyncd_async_attnq);
list_del_init(&kafsasyncd_async_busyq);
/* dequeue kafsasyncd from all their wait queues */
list_for_each(_p,&kafsasyncd_async_attnq) {
afs_async_op_t *op = list_entry(_p,afs_async_op_t,link);
op->call->app_attn_func = kafsasyncd_null_call_attn_func;
op->call->app_error_func = kafsasyncd_null_call_error_func;
remove_wait_queue(&op->call->waitq,&op->waiter);
}
spin_unlock(&kafsasyncd_async_lock);
/* abort all the operations */
while (!list_empty(&kafsasyncd_async_attnq)) {
afs_async_op_t *op = list_entry(_p,afs_async_op_t,link);
list_del_init(&op->link);
rxrpc_call_abort(op->call,-EIO);
rxrpc_put_call(op->call);
op->call = NULL;
op->ops->discard(op);
}
/* and that's all */
_leave("");
complete_and_exit(&kafsasyncd_dead,0);
} /* end kafsasyncd() */
/*****************************************************************************/
/*
* begin an operation
* - place operation on busy queue
*/
void afs_kafsasyncd_begin_op(afs_async_op_t *op)
{
_enter("");
spin_lock(&kafsasyncd_async_lock);
init_waitqueue_entry(&op->waiter,kafsasyncd_task);
list_del(&op->link);
list_add_tail(&op->link,&kafsasyncd_async_busyq);
spin_unlock(&kafsasyncd_async_lock);
_leave("");
} /* end afs_kafsasyncd_begin_op() */
/*****************************************************************************/
/*
* request attention for an operation
* - move to attention queue
*/
void afs_kafsasyncd_attend_op(afs_async_op_t *op)
{
_enter("");
spin_lock(&kafsasyncd_async_lock);
list_del(&op->link);
list_add_tail(&op->link,&kafsasyncd_async_attnq);
spin_unlock(&kafsasyncd_async_lock);
wake_up(&kafsasyncd_sleepq);
_leave("");
} /* end afs_kafsasyncd_attend_op() */
/*****************************************************************************/
/*
* terminate an operation
* - remove from either queue
*/
void afs_kafsasyncd_terminate_op(afs_async_op_t *op)
{
_enter("");
spin_lock(&kafsasyncd_async_lock);
list_del_init(&op->link);
spin_unlock(&kafsasyncd_async_lock);
wake_up(&kafsasyncd_sleepq);
_leave("");
} /* end afs_kafsasyncd_terminate_op() */
/* kafsasyncd.h: AFS asynchronous operation daemon
*
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef _LINUX_AFS_KAFSASYNCD_H
#define _LINUX_AFS_KAFSASYNCD_H
#include "types.h"
struct afs_async_op_ops {
void (*attend)(afs_async_op_t *op);
void (*discard)(afs_async_op_t *op);
};
/*****************************************************************************/
/*
* asynchronous operation record
*/
struct afs_async_op
{
struct list_head link;
afs_server_t *server; /* server being contacted */
struct rxrpc_call *call; /* RxRPC call performing op */
wait_queue_t waiter; /* wait queue for kafsasyncd */
const struct afs_async_op_ops *ops; /* operations */
};
static inline void afs_async_op_init(afs_async_op_t *op, const struct afs_async_op_ops *ops)
{
INIT_LIST_HEAD(&op->link);
op->call = NULL;
op->ops = ops;
}
extern int afs_kafsasyncd_start(void);
extern void afs_kafsasyncd_stop(void);
extern void afs_kafsasyncd_begin_op(afs_async_op_t *op);
extern void afs_kafsasyncd_attend_op(afs_async_op_t *op);
extern void afs_kafsasyncd_terminate_op(afs_async_op_t *op);
#endif /* _LINUX_AFS_KAFSASYNCD_H */
/* kafstimod.c: AFS timeout daemon
*
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/completion.h>
#include "cell.h"
#include "volume.h"
#include "kafstimod.h"
#include <asm/errno.h>
#include "internal.h"
static DECLARE_COMPLETION(kafstimod_alive);
static DECLARE_COMPLETION(kafstimod_dead);
static DECLARE_WAIT_QUEUE_HEAD(kafstimod_sleepq);
static int kafstimod_die;
static LIST_HEAD(kafstimod_list);
static spinlock_t kafstimod_lock = SPIN_LOCK_UNLOCKED;
static int kafstimod(void *arg);
/*****************************************************************************/
/*
* start the timeout daemon
*/
int afs_kafstimod_start(void)
{
int ret;
ret = kernel_thread(kafstimod,NULL,0);
if (ret<0)
return ret;
wait_for_completion(&kafstimod_alive);
return ret;
} /* end afs_kafstimod_start() */
/*****************************************************************************/
/*
* stop the timeout daemon
*/
void afs_kafstimod_stop(void)
{
/* get rid of my daemon */
kafstimod_die = 1;
wake_up(&kafstimod_sleepq);
wait_for_completion(&kafstimod_dead);
} /* end afs_kafstimod_stop() */
/*****************************************************************************/
/*
* timeout processing daemon
*/
static int kafstimod(void *arg)
{
DECLARE_WAITQUEUE(myself,current);
afs_timer_t *timer;
printk("kAFS: Started kafstimod %d\n",current->pid);
strcpy(current->comm,"kafstimod");
daemonize();
complete(&kafstimod_alive);
/* only certain signals are of interest */
spin_lock_irq(&current->sig->siglock);
siginitsetinv(&current->blocked,0);
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,3)
recalc_sigpending();
#else
recalc_sigpending(current);
#endif
spin_unlock_irq(&current->sig->siglock);
/* loop around looking for things to attend to */
loop:
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&kafstimod_sleepq,&myself);
for (;;) {
unsigned long jif;
signed long timeout;
/* deal with the server being asked to die */
if (kafstimod_die) {
remove_wait_queue(&kafstimod_sleepq,&myself);
_leave("");
complete_and_exit(&kafstimod_dead,0);
}
/* discard pending signals */
afs_discard_my_signals();
/* work out the time to elapse before the next event */
spin_lock(&kafstimod_lock);
if (list_empty(&kafstimod_list)) {
timeout = MAX_SCHEDULE_TIMEOUT;
}
else {
timer = list_entry(kafstimod_list.next,afs_timer_t,link);
timeout = timer->timo_jif;
jif = jiffies;
if (time_before_eq(timeout,jif))
goto immediate;
else {
timeout = (long)timeout - (long)jiffies;
}
}
spin_unlock(&kafstimod_lock);
schedule_timeout(timeout);
set_current_state(TASK_INTERRUPTIBLE);
}
/* the thing on the front of the queue needs processing
* - we come here with the lock held and timer pointing to the expired entry
*/
immediate:
remove_wait_queue(&kafstimod_sleepq,&myself);
set_current_state(TASK_RUNNING);
_debug("@@@ Begin Timeout of %p",timer);
/* dequeue the timer */
list_del_init(&timer->link);
spin_unlock(&kafstimod_lock);
/* call the timeout function */
timer->ops->timed_out(timer);
_debug("@@@ End Timeout");
goto loop;
} /* end kafstimod() */
/*****************************************************************************/
/*
* (re-)queue a timer
*/
void afs_kafstimod_add_timer(afs_timer_t *timer, unsigned long timeout)
{
struct list_head *_p;
afs_timer_t *ptimer;
_enter("%p,%lu",timer,timeout);
spin_lock(&kafstimod_lock);
list_del(&timer->link);
/* the timer was deferred or reset - put it back in the queue at the right place */
timer->timo_jif = jiffies + timeout;
list_for_each(_p,&kafstimod_list) {
ptimer = list_entry(_p,afs_timer_t,link);
if (time_before(timer->timo_jif,ptimer->timo_jif))
break;
}
list_add_tail(&timer->link,_p); /* insert before stopping point */
spin_unlock(&kafstimod_lock);
wake_up(&kafstimod_sleepq);
_leave("");
} /* end afs_kafstimod_queue_vlocation() */
/*****************************************************************************/
/*
* dequeue a timer
* - returns 0 if the timer was deleted or -ENOENT if it wasn't queued
*/
int afs_kafstimod_del_timer(afs_timer_t *timer)
{
int ret = 0;
_enter("%p",timer);
spin_lock(&kafstimod_lock);
if (list_empty(&timer->link))
ret = -ENOENT;
else
list_del_init(&timer->link);
spin_unlock(&kafstimod_lock);
wake_up(&kafstimod_sleepq);
_leave(" = %d",ret);
return ret;
} /* end afs_kafstimod_del_timer() */
/* kafstimod.h: AFS timeout daemon
*
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef _LINUX_AFS_KAFSTIMOD_H
#define _LINUX_AFS_KAFSTIMOD_H
#include "types.h"
struct afs_timer_ops {
/* called when the front of the timer queue has timed out */
void (*timed_out)(struct afs_timer *timer);
};
/*****************************************************************************/
/*
* AFS timer/timeout record
*/
struct afs_timer
{
struct list_head link; /* link in timer queue */
unsigned long timo_jif; /* timeout time */
const struct afs_timer_ops *ops; /* timeout expiry function */
};
static inline void afs_timer_init(afs_timer_t *timer, const struct afs_timer_ops *ops)
{
INIT_LIST_HEAD(&timer->link);
timer->ops = ops;
}
extern int afs_kafstimod_start(void);
extern void afs_kafstimod_stop(void);
extern void afs_kafstimod_add_timer(afs_timer_t *timer, unsigned long timeout);
extern int afs_kafstimod_del_timer(afs_timer_t *timer);
#endif /* _LINUX_AFS_KAFSTIMOD_H */
/* main.c: AFS client file system
*
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/completion.h>
#include <rxrpc/rxrpc.h>
#include <rxrpc/transport.h>
#include <rxrpc/call.h>
#include <rxrpc/peer.h>
#include "cell.h"
#include "server.h"
#include "fsclient.h"
#include "cmservice.h"
#include "kafstimod.h"
#include "kafsasyncd.h"
#include "internal.h"
struct rxrpc_transport *afs_transport;
static int afs_init(void);
static void afs_exit(void);
static int afs_adding_peer(struct rxrpc_peer *peer);
static void afs_discarding_peer(struct rxrpc_peer *peer);
module_init(afs_init);
module_exit(afs_exit);
MODULE_DESCRIPTION("AFS Client File System");
MODULE_AUTHOR("Red Hat, Inc.");
MODULE_LICENSE("GPL");
static struct rxrpc_peer_ops afs_peer_ops = {
.adding = afs_adding_peer,
.discarding = afs_discarding_peer,
};
struct list_head afs_cb_hash_tbl[AFS_CB_HASH_COUNT];
spinlock_t afs_cb_hash_lock = SPIN_LOCK_UNLOCKED;
/*****************************************************************************/
/*
* initialise the AFS client FS module
*/
static int afs_init(void)
{
int loop, ret;
printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 registering.\n");
/* initialise the callback hash table */
spin_lock_init(&afs_cb_hash_lock);
for (loop=AFS_CB_HASH_COUNT-1; loop>=0; loop--)
INIT_LIST_HEAD(&afs_cb_hash_tbl[loop]);
/* register the /proc stuff */
ret = afs_proc_init();
if (ret<0)
return ret;
/* initialise the cell DB */
ret = afs_cell_init();
if (ret<0)
goto error;
/* start the timeout daemon */
ret = afs_kafstimod_start();
if (ret<0)
goto error;
/* start the async operation daemon */
ret = afs_kafsasyncd_start();
if (ret<0)
goto error_kafstimod;
/* create the RxRPC transport */
ret = rxrpc_create_transport(7001,&afs_transport);
if (ret<0)
goto error_kafsasyncd;
afs_transport->peer_ops = &afs_peer_ops;
/* register the filesystems */
ret = afs_fs_init();
if (ret<0)
goto error_transport;
return ret;
error_transport:
rxrpc_put_transport(afs_transport);
error_kafsasyncd:
afs_kafsasyncd_stop();
error_kafstimod:
afs_kafstimod_stop();
error:
afs_cell_purge();
afs_proc_cleanup();
printk(KERN_ERR "kAFS: failed to register: %d\n",ret);
return ret;
} /* end afs_init() */
/*****************************************************************************/
/*
* clean up on module removal
*/
static void afs_exit(void)
{
printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 unregistering.\n");
afs_fs_exit();
rxrpc_put_transport(afs_transport);
afs_kafstimod_stop();
afs_kafsasyncd_stop();
afs_cell_purge();
afs_proc_cleanup();
} /* end afs_exit() */
/*****************************************************************************/
/*
* notification that new peer record is being added
* - called from krxsecd
* - return an error to induce an abort
* - mustn't sleep (caller holds an rwlock)
*/
static int afs_adding_peer(struct rxrpc_peer *peer)
{
afs_server_t *server;
int ret;
_debug("kAFS: Adding new peer %08x\n",ntohl(peer->addr.s_addr));
/* determine which server the peer resides in (if any) */
ret = afs_server_find_by_peer(peer,&server);
if (ret<0)
return ret; /* none that we recognise, so abort */
_debug("Server %p{u=%d}\n",server,atomic_read(&server->usage));
_debug("Cell %p{u=%d}\n",server->cell,atomic_read(&server->cell->usage));
/* cross-point the structs under a global lock */
spin_lock(&afs_server_peer_lock);
peer->user = server;
server->peer = peer;
spin_unlock(&afs_server_peer_lock);
afs_put_server(server);
return 0;
} /* end afs_adding_peer() */
/*****************************************************************************/
/*
* notification that a peer record is being discarded
* - called from krxiod or krxsecd
*/
static void afs_discarding_peer(struct rxrpc_peer *peer)
{
afs_server_t *server;
_enter("%p",peer);
_debug("Discarding peer %08x (rtt=%lu.%lumS)\n",
ntohl(peer->addr.s_addr),
peer->rtt/1000,
peer->rtt%1000);
/* uncross-point the structs under a global lock */
spin_lock(&afs_server_peer_lock);
server = peer->user;
if (server) {
peer->user = NULL;
server->peer = NULL;
//_debug("Server %p{u=%d}\n",server,atomic_read(&server->usage));
//_debug("Cell %p{u=%d}\n",server->cell,atomic_read(&server->cell->usage));
}
spin_unlock(&afs_server_peer_lock);
_leave("");
} /* end afs_discarding_peer() */
/* misc.c: miscellaneous bits
*
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#include "errors.h"
#include "internal.h"
/*****************************************************************************/
/*
* convert an AFS abort code to a Linux error number
*/
int afs_abort_to_error(int abortcode)
{
switch (abortcode) {
case VSALVAGE: return -EIO;
case VNOVNODE: return -ENOENT;
case VNOVOL: return -ENXIO;
case VVOLEXISTS: return -EEXIST;
case VNOSERVICE: return -EIO;
case VOFFLINE: return -ENOENT;
case VONLINE: return -EEXIST;
case VDISKFULL: return -ENOSPC;
case VOVERQUOTA: return -EDQUOT;
case VBUSY: return -EBUSY;
case VMOVED: return -ENXIO;
default: return -EIO;
}
} /* end afs_abort_to_error() */
/* mntpt.c: mountpoint management
*
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/pagemap.h>
#include "volume.h"
#include "vnode.h"
#include "internal.h"
static struct dentry *afs_mntpt_lookup(struct inode *dir, struct dentry *dentry);
static int afs_mntpt_open(struct inode *inode, struct file *file);
struct file_operations afs_mntpt_file_operations = {
.open = afs_mntpt_open,
};
struct inode_operations afs_mntpt_inode_operations = {
.lookup = afs_mntpt_lookup,
.readlink = page_readlink,
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
.getattr = afs_inode_getattr,
#else
.revalidate = afs_inode_revalidate,
#endif
};
/*****************************************************************************/
/*
* check a symbolic link to see whether it actually encodes a mountpoint
* - sets the AFS_VNODE_MOUNTPOINT flag on the vnode appropriately
*/
int afs_mntpt_check_symlink(afs_vnode_t *vnode)
{
struct page *page;
size_t size;
char *buf;
int ret;
_enter("{%u,%u}",vnode->fid.vnode,vnode->fid.unique);
/* read the contents of the symlink into the pagecache */
page = read_cache_page(AFS_VNODE_TO_I(vnode)->i_mapping,0,
(filler_t*)AFS_VNODE_TO_I(vnode)->i_mapping->a_ops->readpage,NULL);
if (IS_ERR(page)) {
ret = PTR_ERR(page);
goto out;
}
ret = -EIO;
wait_on_page_locked(page);
buf = kmap(page);
if (!PageUptodate(page))
goto out_free;
if (PageError(page))
goto out_free;
/* examine the symlink's contents */
size = vnode->status.size;
_debug("symlink to %*.*s",size,size,buf);
if (size>2 &&
(buf[0]=='%' || buf[0]=='#') &&
buf[size-1]=='.'
) {
_debug("symlink is a mountpoint");
spin_lock(&vnode->lock);
vnode->flags |= AFS_VNODE_MOUNTPOINT;
spin_unlock(&vnode->lock);
}
ret = 0;
out_free:
kunmap(page);
page_cache_release(page);
out:
_leave(" = %d",ret);
return ret;
} /* end afs_mntpt_check_symlink() */
/*****************************************************************************/
/*
* no valid lookup procedure on this sort of dir
*/
static struct dentry *afs_mntpt_lookup(struct inode *dir, struct dentry *dentry)
{
return ERR_PTR(-EREMOTE);
} /* end afs_mntpt_lookup() */
/*****************************************************************************/
/*
* no valid open procedure on this sort of dir
*/
static int afs_mntpt_open(struct inode *inode, struct file *file)
{
return -EREMOTE;
} /* end afs_mntpt_open() */
/* mount.h: mount parameters
*
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef _LINUX_AFS_MOUNT_H
#define _LINUX_AFS_MOUNT_H
struct afs_mountdata {
const char *volume; /* name of volume */
const char *cell; /* name of cell containing volume */
const char *cache; /* name of cache block device */
size_t nservers; /* number of server addresses listed */
u_int32_t servers[10]; /* IP addresses of servers in this cell */
};
#endif /* _LINUX_AFS_MOUNT_H */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/* transport.h: AFS transport management
*
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef _LINUX_AFS_TRANSPORT_H
#define _LINUX_AFS_TRANSPORT_H
#include "types.h"
#include <rxrpc/transport.h>
/* the cache manager transport endpoint */
extern struct rxrpc_transport *afs_transport;
#endif /* _LINUX_AFS_TRANSPORT_H */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment