Commit 1540068e authored by Linus Torvalds's avatar Linus Torvalds

- Add QNX4 file system.

parent 07e73e00
......@@ -6478,6 +6478,18 @@ CONFIG_ROMFS_FS
If you don't know whether you need it, then you don't need it: say
N.
QNX filesystem support (EXPERIMENTAL)
CONFIG_QNX4FS_FS
This is the filesystem used by QNX 4. Say Y if you intend to mount
QNX hard disks and floppies.
This filesystem support is also available as a module ( = code which
can be inserted in and removed from the running kernel whenever you
want). The module is called qnx4.o. If you want to compile it as a
module, say M here and read Documentation/modules.txt.
If unsure, say N.
Kernel automounter support
CONFIG_AUTOFS_FS
The automounter is a tool to automatically mount remote filesystems
......
......@@ -10,7 +10,6 @@
#include <linux/init.h> /* Initdata */
#include <asm/uaccess.h> /* copy to/from user */
#include <linux/videodev.h> /* kernel radio structs */
#include <linux/config.h> /* CONFIG_RADIO_MIROPCM20 */
#include "../sound/lowlevel/miroaci.h" /* ACI Control by acimixer */
static int users = 0;
......
......@@ -87,7 +87,6 @@
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/config.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
......
......@@ -77,6 +77,10 @@ if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then
fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'ADFS filesystem support (read only) (EXPERIMENTAL)' CONFIG_ADFS_FS
tristate 'QNX filesystem support (EXPERIMENTAL)' CONFIG_QNX4FS_FS
if [ "$CONFIG_QNX4FS_FS" != "n" ]; then
bool ' QNXFS read-write support (FOR TESTING ONLY)' CONFIG_QNX4FS_RW
fi
fi
bool 'Macintosh partition map support' CONFIG_MAC_PARTITION
endmenu
......@@ -18,7 +18,7 @@ O_OBJS = open.o read_write.o devices.o file_table.o buffer.o \
MOD_LIST_NAME := FS_MODULES
ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \
hpfs sysv smbfs ncpfs ufs affs romfs autofs hfs lockd nfsd \
nls devpts adfs
nls devpts adfs qnx4
ifeq ($(CONFIG_QUOTA),y)
O_OBJS += dquot.o
......@@ -207,6 +207,14 @@ else
endif
endif
ifeq ($(CONFIG_QNX4FS_FS),y)
SUB_DIRS += qnx4
else
ifeq ($(CONFIG_QNX4FS_FS),m)
MOD_SUB_DIRS += qnx4
endif
endif
ifeq ($(CONFIG_AUTOFS_FS),y)
SUB_DIRS += autofs
else
......
......@@ -24,6 +24,7 @@
#include <linux/ufs_fs.h>
#include <linux/romfs_fs.h>
#include <linux/auto_fs.h>
#include <linux/qnx4_fs.h>
#include <linux/ntfs_fs.h>
#include <linux/hfs_fs.h>
#include <linux/devpts_fs.h>
......@@ -148,6 +149,10 @@ static void __init do_sys_setup(void)
init_devpts_fs();
#endif
#ifdef CONFIG_QNX4FS_FS
init_qnx4_fs();
#endif
#ifdef CONFIG_NLS
init_nls();
#endif
......
......@@ -770,7 +770,13 @@ int fs_may_remount_ro(struct super_block *sb)
inode = file->f_dentry->d_inode;
if (!inode || inode->i_sb != sb)
continue;
if (S_ISREG(inode->i_mode) && file->f_mode & FMODE_WRITE)
/* File with pending delete? */
if (inode->i_nlink == 0)
return 0;
/* Writable file? */
if (S_ISREG(inode->i_mode) && (file->f_mode & FMODE_WRITE))
return 0;
}
return 1; /* Tis' cool bro. */
......
......@@ -130,18 +130,34 @@ struct iso9660_options{
static int strnicmp(const char *s1, const char *s2, int len)
{
int n = 0;
while (*s1 && *s2 && (tolower(*s1) == tolower(*s2))) {
s1++; s2++; n++;
if (n == len) return 0;
}
if (*s1 == 0 && *s2 == 0) return 0;
if (*s1 && *s2) {
if (*s1 > *s2) return 1;
return -1;
/* Yes, Virginia, it had better be unsigned */
unsigned char c1, c2;
while (len) {
c1 = *s1; c2 = *s2;
s1++; s2++;
if (!c1)
goto end_of_string1;
if (!c2)
goto end_of_string2;
if (c1 != c2) {
c1 = tolower(c1);
c2 = tolower(c2);
if (c1 != c2)
goto different;
}
len--;
}
if (*s1) return 1;
return -1;
return 0;
end_of_string1:
return c2 ? -1 : 0;
end_of_string2:
return 1;
different:
return c1 < c2 ? -1 : 1;
}
/*
......
......@@ -392,14 +392,15 @@ printk("exp_unexport: ino mismatch, %ld not %ld\n", exp->ex_ino, nxp->ex_ino);
int
exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino, struct knfs_fh *f)
{
struct svc_export *exp = NULL;
struct svc_fh fh;
struct svc_export *exp;
struct dentry *dentry;
struct inode *inode;
struct svc_fh fh;
dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", clp->cl_ident, dev, ino);
if (!(exp = exp_get(clp, dev, ino)))
exp = exp_get(clp, dev, ino);
if (!exp)
return -EPERM;
dentry = exp->ex_dentry;
......@@ -414,8 +415,11 @@ exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino, struct knfs_fh *f)
dev, ino, inode->i_dev, inode->i_ino);
}
dget(dentry);
fh_compose(&fh, exp, dentry);
/*
* fh must be initialized before calling fh_compose
*/
fh_init(&fh);
fh_compose(&fh, exp, dget(dentry));
memcpy(f, &fh.fh_handle, sizeof(struct knfs_fh));
fh_put(&fh);
......
......@@ -22,12 +22,13 @@
static u32
nlm_fopen(struct svc_rqst *rqstp, struct knfs_fh *f, struct file *filp)
{
struct svc_fh fh;
u32 nfserr;
struct svc_fh fh;
/* must initialize before using! */
fh_init(&fh);
fh.fh_handle = *f;
fh.fh_export = NULL;
fh.fh_dverified = 0;
nfserr = nfsd_open(rqstp, &fh, S_IFREG, 0, filp);
if (!nfserr)
......
......@@ -1107,7 +1107,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry)
dprintk("nfsd: fh_compose(exp %x/%ld %s/%s, ino=%ld)\n",
exp->ex_dev, exp->ex_ino,
dentry->d_parent->d_name.name, dentry->d_name.name,
parent->d_name.name, dentry->d_name.name,
(inode ? inode->i_ino : 0));
/*
......@@ -1115,7 +1115,12 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry)
* may not be done on error paths, but the cleanup must call fh_put.
* Fix this soon!
*/
if (fhp->fh_dverified || fhp->fh_locked || fhp->fh_dentry) {
printk(KERN_ERR "fh_compose: fh %s/%s not initialized!\n",
parent->d_name.name, dentry->d_name.name);
}
fh_init(fhp);
fhp->fh_handle.fh_dcookie = dentry;
if (inode) {
fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino);
......
Last update: 03-07-1998
- Files in a subdir can't be accessed, I think that the inode information
is not correctly copied at some point. Solved 06-06-1998, Richard.
- At some point the mounted device can't be unmounted. I think that somewhere
in the code a buffer is not given free.
- Make the '..' entry work, I give it a great chance that the above bug
(not given free) has something to do with this one, after a 'ls -l'
the mounted device can't be unmounted and that's where the '..' entry
is accessed.
Seems to be solved 21-06-1998, Frank.
- File read function not correct, after the first block it goes beserk.
Solved 21-06-1998, Frank.
- This fs will not work if not built as a module.
Solved 25-06-1998, Frank.
- Write/truncate/delete functions don't update the bitmap.
#
# Makefile for the linux qnx4-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).
#
# Note 2! The CFLAGS definitions are now in the main makefile...
O_TARGET := qnx4.o
O_OBJS := inode.o dir.o namei.o file.o bitmap.o symlinks.o truncate.o \
fsync.o
M_OBJS := $(O_TARGET)
include $(TOPDIR)/Rules.make
This is a snapshot of the QNX4 filesystem for Linux.
Please send diffs and remarks to <qnxfs@rtc-one.net> .
Credits :
Richard "Scuba" A. Frowijn <scuba@wxs.nl>
Frank "Jedi/Sector One" Denis <j@4u.net>
Name : QNX4 TODO list
Last update: 29-06-1998
- qnx4_checkroot (inode.c), currently there's a look for the '/' in
the root direntry, if so then the current mounted device is a qnx4
partition. This has to be rewritten with a look for 'QNX4' in the
bootblock, it seems to me the savest way to ensure that the mounted
device is in fact a QNX4 partition.
Done 20-06-1998, Frank. But some disks (like QNX install floppies)
don't have 'QNX4' in their bootblock.
- Bitmap functions. To find out the free space, largest free block, etc.
Partly done (RO), Richard, 05/06/1998. Optimized 20-06-1998, Frank.
- Symbolic links. symlinks.c have to be rewritten.
- Extended files.
- Complete write, unlink and truncate functions : the bitmap should be
updated.
- Porting to linux 2.1.99+ with dcache support. 20-06-1998, Frank.
- Don't rewrite the file_read function : use the generic_file_read hook,
and write readpage instead. Done on 21-06-1998, Frank.
- Write dinit and dcheck.
- Solving the bugs.
- Use le32_to_cpu and vice-versa for portability.
/*
* QNX4 file system, Linux implementation.
*
* Version : 0.1
*
* Using parts of the xiafs filesystem.
*
* History :
*
* 28-05-1998 by Richard Frowijn : first release.
* 20-06-1998 by Frank Denis : basic optimisations.
* 25-06-1998 by Frank Denis : qnx4_is_free, qnx4_set_bitmap, qnx4_bmap .
* 28-06-1998 by Frank Denis : qnx4_free_inode (to be fixed) .
*/
#include <linux/sched.h>
#include <linux/qnx4_fs.h>
#include <linux/stat.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <asm/bitops.h>
int qnx4_new_block(struct super_block *sb)
{
return 0;
}
void count_bits(const register char *bmPart, register int size,
int *const tf)
{
char b;
int tot = *tf;
if (size > QNX4_BLOCK_SIZE) {
size = QNX4_BLOCK_SIZE;
}
do {
b = *bmPart++;
if ((b & 1) == 0)
tot++;
if ((b & 2) == 0)
tot++;
if ((b & 4) == 0)
tot++;
if ((b & 8) == 0)
tot++;
if ((b & 16) == 0)
tot++;
if ((b & 32) == 0)
tot++;
if ((b & 64) == 0)
tot++;
if ((b & 128) == 0)
tot++;
size--;
} while (size != 0);
*tf = tot;
}
unsigned long qnx4_count_free_blocks(struct super_block *sb)
{
int start = sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk - 1;
int total = 0;
int total_free = 0;
int offset = 0;
int size = sb->u.qnx4_sb.BitMap->di_size;
struct buffer_head *bh;
while (total < size) {
if ((bh = bread(sb->s_dev, start + offset, QNX4_BLOCK_SIZE)) == NULL) {
printk("qnx4: I/O error in counting free blocks\n");
break;
}
count_bits(bh->b_data, size - total, &total_free);
brelse(bh);
total += QNX4_BLOCK_SIZE;
}
return total_free;
}
unsigned long qnx4_count_free_inodes(struct super_block *sb)
{
return qnx4_count_free_blocks(sb) * QNX4_INODES_PER_BLOCK; /* FIXME */
}
int qnx4_is_free(struct super_block *sb, int block)
{
int start = sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk - 1;
int size = sb->u.qnx4_sb.BitMap->di_size;
struct buffer_head *bh;
const char *g;
int ret = -EIO;
start += block / (QNX4_BLOCK_SIZE * 8);
QNX4DEBUG(("qnx4: is_free requesting block [%lu], bitmap in block [%lu]\n",
(unsigned long) block, (unsigned long) start));
(void) size; /* CHECKME */
bh = bread(sb->s_dev, start, QNX4_BLOCK_SIZE);
if (bh == NULL) {
return -EIO;
}
g = bh->b_data + (block % QNX4_BLOCK_SIZE);
if (((*g) & (1 << (block % 8))) == 0) {
QNX4DEBUG(("qnx4: is_free -> block is free\n"));
ret = 1;
} else {
QNX4DEBUG(("qnx4: is_free -> block is busy\n"));
ret = 0;
}
brelse(bh);
return ret;
}
int qnx4_bmap(struct inode *inode, int block)
{
QNX4DEBUG(("qnx4: bmap on block [%d]\n", block));
if (block < 0) {
return 0;
}
return !qnx4_is_free(inode->i_sb, block);
}
#ifdef CONFIG_QNX4FS_RW
int qnx4_set_bitmap(struct super_block *sb, int block, int busy)
{
int start = sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk - 1;
int size = sb->u.qnx4_sb.BitMap->di_size;
struct buffer_head *bh;
char *g;
start += block / (QNX4_BLOCK_SIZE * 8);
QNX4DEBUG(("qnx4: set_bitmap requesting block [%lu], bitmap in block [%lu]\n",
(unsigned long) block, (unsigned long) start));
(void) size; /* CHECKME */
bh = bread(sb->s_dev, start, QNX4_BLOCK_SIZE);
if (bh == NULL) {
return -EIO;
}
g = bh->b_data + (block % QNX4_BLOCK_SIZE);
if (busy == 0) {
(*g) &= ~(1 << (block % 8));
} else {
(*g) |= (1 << (block % 8));
}
mark_buffer_dirty(bh, 1);
brelse(bh);
return 0;
}
static void qnx4_clear_inode(struct inode *inode)
{
struct qnx4_inode_info *qnx4_ino = &inode->u.qnx4_i;
memset(qnx4_ino->i_reserved, 0, sizeof qnx4_ino->i_reserved);
qnx4_ino->i_size = 0;
qnx4_ino->i_num_xtnts = 0;
qnx4_ino->i_mode = 0;
qnx4_ino->i_status = 0;
}
void qnx4_free_inode(struct inode *inode)
{
if (!inode) {
return;
}
if (!inode->i_dev) {
printk("free_inode: inode has no device\n");
return;
}
if (inode->i_count > 1) {
printk("free_inode: inode has count=%d\n", inode->i_count);
return;
}
if (inode->i_nlink) {
printk("free_inode: inode has nlink=%d\n", inode->i_nlink);
return;
}
if (!inode->i_sb) {
printk("free_inode: inode on nonexistent device\n");
return;
}
if (inode->i_ino < 1) {
printk("free_inode: inode 0 or nonexistent inode\n");
return;
}
qnx4_clear_inode(inode);
clear_inode(inode);
}
#endif
/*
* QNX4 file system, Linux implementation.
*
* Version : 0.1
*
* Using parts of the xiafs filesystem.
*
* History :
*
* 28-05-1998 by Richard Frowijn : first release.
* 20-06-1998 by Frank Denis : Linux 2.1.99+ & dcache support.
*/
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/qnx4_fs.h>
#include <linux/stat.h>
#include <asm/segment.h>
static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir);
static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
struct inode *inode = filp->f_dentry->d_inode;
unsigned int offset;
struct buffer_head *bh;
struct qnx4_inode_entry *de;
long blknum;
int i;
int size;
blknum = inode->u.qnx4_i.i_first_xtnt.xtnt_blk - 1 +
((filp->f_pos >> 6) >> 3);
if (!inode || !inode->i_sb || !S_ISDIR(inode->i_mode)) {
return -EBADF;
}
QNX4DEBUG(("qnx4_readdir:i_size = %ld\n", (long) inode->i_size));
QNX4DEBUG(("filp->f_pos = %ld\n", (long) filp->f_pos));
QNX4DEBUG(("BlkNum = %ld\n", (long) blknum));
while (filp->f_pos < inode->i_size) {
bh = bread(inode->i_dev, blknum, QNX4_BLOCK_SIZE);
i = (filp->f_pos - (((filp->f_pos >> 6) >> 3) << 9)) & 0x3f;
while (i < QNX4_INODES_PER_BLOCK) {
offset = i * QNX4_DIR_ENTRY_SIZE;
de = (struct qnx4_inode_entry *) (bh->b_data + offset);
size = strlen(de->di_fname);
if (size) {
QNX4DEBUG(("qnx4_readdir:%s\n", de->di_fname));
if ((de->di_mode) || (de->di_status == QNX4_FILE_LINK)) {
if (de->di_status) {
if (filldir(dirent, de->di_fname, size, filp->f_pos, de->di_first_xtnt.xtnt_blk) < 0) {
brelse(bh);
return 0;
}
}
}
}
i++;
filp->f_pos += QNX4_DIR_ENTRY_SIZE;
}
brelse(bh);
blknum++;
}
UPDATE_ATIME(inode);
return 0;
}
static struct file_operations qnx4_dir_operations =
{
NULL, /* lseek - default */
NULL, /* read */
NULL, /* write - bad */
qnx4_readdir, /* readdir */
NULL, /* poll - default */
NULL, /* ioctl - default */
NULL, /* mmap */
NULL, /* no special open code */
NULL, /* no special flush code */
NULL, /* no special release code */
file_fsync, /* default fsync */
NULL, /* default fasync */
NULL, /* default check_media_change */
NULL, /* default revalidate */
};
struct inode_operations qnx4_dir_inode_operations =
{
&qnx4_dir_operations,
#ifdef CONFIG_QNX4FS_RW
qnx4_create,
#else
NULL, /* create */
#endif
qnx4_lookup,
NULL, /* link */
#ifdef CONFIG_QNX4FS_RW
qnx4_unlink, /* unlink */
#else
NULL,
#endif
NULL, /* symlink */
NULL, /* mkdir */
#ifdef CONFIG_QNX4FS_RW
qnx4_rmdir, /* rmdir */
#else
NULL,
#endif
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL, /* permission */
NULL /* smap */
};
/*
* QNX4 file system, Linux implementation.
*
* Version : 0.1
*
* Using parts of the xiafs filesystem.
*
* History :
*
* 25-05-1998 by Richard Frowijn : first release.
* 21-06-1998 by Frank Denis : wrote qnx4_readpage to use generic_file_read.
* 27-06-1998 by Frank Denis : file overwriting.
*/
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/qnx4_fs.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/malloc.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/locks.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
#include <linux/fs.h>
#include <linux/qnx4_fs.h>
static int qnx4_readpage(struct file *file, struct page *page);
#ifdef CONFIG_QNX4FS_RW
static ssize_t qnx4_file_write(struct file *filp, const char *buf,
size_t count, loff_t * ppos)
{
struct dentry *dentry = filp->f_dentry;
struct inode *inode = dentry->d_inode;
struct qnx4_inode_info *qnx4_ino;
struct buffer_head *bh;
ssize_t result = -EBUSY, c;
off_t pos;
unsigned long start, block, extent_end;
char *p;
QNX4DEBUG(("qnx4: file_write(%s/%s (%d), %lu@%lu)\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
inode->i_count, (unsigned long) count, (unsigned long) *ppos));
if (inode == NULL) {
printk("qnx4: NULL inode for file_write\n");
return -EINVAL;
}
qnx4_ino = &inode->u.qnx4_i;
if (S_ISREG(inode->i_mode) == 0) {
printk("qnx4: write to non-file, mode %07o\n", inode->i_mode);
return -EINVAL;
}
if (count == 0) {
goto out;
}
if (filp->f_flags & O_APPEND) {
pos = inode->i_size;
} else {
pos = *ppos;
}
start = qnx4_ino->i_first_xtnt.xtnt_blk + ((pos >> 9) * 0) - 1;
result = 0;
extent_end = start + qnx4_ino->i_first_xtnt.xtnt_size - 1;
QNX4DEBUG(("qnx4: extent length : [%lu] bytes\n",
qnx4_ino->i_first_xtnt.xtnt_size));
while (result < count) {
block = start + pos / QNX4_BLOCK_SIZE;
if (block > extent_end) {
if (qnx4_is_free(inode->i_sb, block) <= 0) {
printk("qnx4: next inode is busy -> write aborted.\n");
result = -ENOSPC;
break;
}
}
if ((bh = bread(inode->i_dev, block,
QNX4_BLOCK_SIZE)) == NULL) {
printk("qnx4: I/O error on write.\n");
result = -EIO;
goto out;
}
if (bh == NULL) {
if (result != 0) {
result = -ENOSPC;
}
break;
}
if (block > extent_end) {
qnx4_set_bitmap(inode->i_sb, block, 1);
extent_end++;
qnx4_ino->i_first_xtnt.xtnt_size = extent_end - start + 1;
}
c = QNX4_BLOCK_SIZE - (pos % QNX4_BLOCK_SIZE);
if (c > count - result) {
c = count - result;
}
if (c != QNX4_BLOCK_SIZE && buffer_uptodate(bh) == 0) {
ll_rw_block(WRITE, 1, &bh);
wait_on_buffer(bh);
if (buffer_uptodate(bh) == 0) {
brelse(bh);
if (result != 0) {
result = -EIO;
}
break;
}
}
p = bh->b_data + (pos % QNX4_BLOCK_SIZE);
c -= copy_from_user(p, buf, c);
if (c == 0) {
brelse(bh);
if (result == 0) {
result = -EFAULT;
}
break;
}
update_vm_cache(inode, pos, p, c);
mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh, 0);
brelse(bh);
pos += c;
buf += c;
result += c;
}
if (pos > inode->i_size) {
inode->i_size = pos;
}
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
*ppos = pos;
mark_inode_dirty(inode);
out:
return result;
}
#endif
/*
* We have moostly NULL's here: the current defaults are ok for
* the qnx4 filesystem.
*/
static struct file_operations qnx4_file_operations =
{
NULL, /* lseek - default */
generic_file_read, /* read */
#ifdef CONFIG_QNX4FS_RW
qnx4_file_write, /* write */
#else
NULL,
#endif
NULL, /* readdir - bad */
NULL, /* poll - default */
NULL, /* ioctl - default */
generic_file_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* no special flush code */
NULL, /* release */
#ifdef CONFIG_QNX4FS_RW
qnx4_sync_file, /* fsync */
#else
NULL,
#endif
NULL, /* fasync */
NULL, /* check_media_change */
NULL, /* revalidate */
NULL /* lock */
};
struct inode_operations qnx4_file_inode_operations =
{
&qnx4_file_operations, /* default file operations */
#ifdef CONFIG_QNX4FS_RW
qnx4_create, /* create */
#else
NULL,
#endif
NULL, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
qnx4_readpage, /* readpage */
NULL, /* writepage */
qnx4_bmap, /* bmap */
#ifdef CONFIG_QNX4FS_RW
qnx4_truncate, /* truncate */
#else
NULL,
#endif
NULL, /* permission */
NULL /* smap */
};
static int qnx4_readpage(struct file *file, struct page *page)
{
struct dentry *dentry = file->f_dentry;
struct inode *inode = dentry->d_inode;
struct qnx4_inode_info *qnx4_ino = &inode->u.qnx4_i;
unsigned long buf;
unsigned long offset, avail, readlen;
unsigned long start;
unsigned long count;
struct buffer_head *bh;
int res = -EIO;
QNX4DEBUG(("qnx4: readpage offset=[%ld]\n", (long) page->offset));
if (qnx4_ino->i_xblk != 0) {
printk("qnx4: sorry, this file is extended, don't know how to handle it (yet) !\n");
return -EIO;
}
atomic_inc(&page->count);
set_bit(PG_locked, &page->flags);
buf = page_address(page);
clear_bit(PG_uptodate, &page->flags);
clear_bit(PG_error, &page->flags);
offset = page->offset;
if (offset < inode->i_size) {
res = 0;
avail = inode->i_size - offset;
readlen = MIN(avail, PAGE_SIZE);
start = qnx4_ino->i_first_xtnt.xtnt_blk + (offset >> 9) - 1;
count = PAGE_SIZE / QNX4_BLOCK_SIZE;
do {
QNX4DEBUG(("qnx4: reading page starting at [%ld]\n", (long) start));
if ((bh = bread(inode->i_dev, start, QNX4_BLOCK_SIZE)) == NULL) {
printk("qnx4: data corrupted or I/O error.\n");
res = -EIO;
} else {
memcpy((void *) buf, bh->b_data, QNX4_BLOCK_SIZE);
}
buf += QNX4_BLOCK_SIZE;
start++;
count--;
} while (count != 0);
}
if (res != 0) {
set_bit(PG_error, &page->flags);
memset((void *) buf, 0, PAGE_SIZE);
} else {
set_bit(PG_uptodate, &page->flags);
}
clear_bit(PG_locked, &page->flags);
wake_up(&page->wait);
/* free_page(buf); */
return res;
}
/*
* QNX4 file system, Linux implementation.
*
* Version : 0.1
*
* Using parts of the xiafs filesystem.
*
* History :
*
* 24-03-1998 by Richard Frowijn : first release.
*/
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <linux/locks.h>
#include <linux/fs.h>
#include <linux/qnx4_fs.h>
#include <asm/segment.h>
#include <asm/system.h>
#define blocksize QNX4_BLOCK_SIZE
/*
* The functions for qnx4 fs file synchronization.
*/
#ifdef CONFIG_QNX4FS_RW
static int sync_block(struct inode *inode, unsigned short *block, int wait)
{
struct buffer_head *bh;
unsigned short tmp;
if (!*block)
return 0;
tmp = *block;
bh = get_hash_table(inode->i_dev, *block, blocksize);
if (!bh)
return 0;
if (*block != tmp) {
brelse(bh);
return 1;
}
if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
brelse(bh);
return -1;
}
if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) {
brelse(bh);
return 0;
}
ll_rw_block(WRITE, 1, &bh);
bh->b_count--;
return 0;
}
static int sync_iblock(struct inode *inode, unsigned short *iblock,
struct buffer_head **bh, int wait)
{
int rc;
unsigned short tmp;
*bh = NULL;
tmp = *iblock;
if (!tmp)
return 0;
rc = sync_block(inode, iblock, wait);
if (rc)
return rc;
*bh = bread(inode->i_dev, tmp, blocksize);
if (tmp != *iblock) {
brelse(*bh);
*bh = NULL;
return 1;
}
if (!*bh)
return -1;
return 0;
}
static int sync_direct(struct inode *inode, int wait)
{
int i;
int rc, err = 0;
for (i = 0; i < 7; i++) {
rc = sync_block(inode,
(unsigned short *) inode->u.qnx4_i.i_first_xtnt.xtnt_blk + i, wait);
if (rc > 0)
break;
if (rc)
err = rc;
}
return err;
}
static int sync_indirect(struct inode *inode, unsigned short *iblock, int wait)
{
int i;
struct buffer_head *ind_bh;
int rc, err = 0;
rc = sync_iblock(inode, iblock, &ind_bh, wait);
if (rc || !ind_bh)
return rc;
for (i = 0; i < 512; i++) {
rc = sync_block(inode,
((unsigned short *) ind_bh->b_data) + i,
wait);
if (rc > 0)
break;
if (rc)
err = rc;
}
brelse(ind_bh);
return err;
}
static int sync_dindirect(struct inode *inode, unsigned short *diblock,
int wait)
{
int i;
struct buffer_head *dind_bh;
int rc, err = 0;
rc = sync_iblock(inode, diblock, &dind_bh, wait);
if (rc || !dind_bh)
return rc;
for (i = 0; i < 512; i++) {
rc = sync_indirect(inode,
((unsigned short *) dind_bh->b_data) + i,
wait);
if (rc > 0)
break;
if (rc)
err = rc;
}
brelse(dind_bh);
return err;
}
int qnx4_sync_file(struct file *file, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
int wait, err = 0;
(void) file;
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
S_ISLNK(inode->i_mode)))
return -EINVAL;
for (wait = 0; wait <= 1; wait++) {
err |= sync_direct(inode, wait);
}
err |= qnx4_sync_inode(inode);
return (err < 0) ? -EIO : 0;
}
#endif
/*
* QNX4 file system, Linux implementation.
*
* Version : 0.1
*
* Using parts of the xiafs filesystem.
*
* History :
*
* 01-06-1998 by Richard Frowijn : first release.
* 20-06-1998 by Frank Denis : Linux 2.1.99+ support, boot signature, misc.
* 30-06-1998 by Frank Denis : first step to write inodes.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/malloc.h>
#include <linux/qnx4_fs.h>
#include <linux/fs.h>
#include <linux/locks.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#define QNX4_VERSION 4
#define QNX4_BMNAME ".bitmap"
#define CHECK_BOOT_SIGNATURE 0
static struct super_operations qnx4_sops;
#ifdef CONFIG_QNX4FS_RW
int qnx4_sync_inode(struct inode *inode)
{
int err = 0;
# if 0
struct buffer_head *bh;
bh = qnx4_update_inode(inode);
if (bh && buffer_dirty(bh))
{
ll_rw_block(WRITE, 1, &bh);
wait_on_buffer(bh);
if (buffer_req(bh) && !buffer_uptodate(bh))
{
printk ("IO error syncing qnx4 inode [%s:%08lx]\n",
kdevname(inode->i_dev), inode->i_ino);
err = -1;
}
brelse (bh);
} else if (!bh) {
err = -1;
}
# endif
return err;
}
static void qnx4_delete_inode(struct inode *inode)
{
QNX4DEBUG(("qnx4: deleting inode [%lu]\n", (unsigned long) inode->i_ino));
inode->i_size = 0;
qnx4_truncate(inode);
qnx4_free_inode(inode);
}
static void qnx4_write_super(struct super_block *sb)
{
QNX4DEBUG(("qnx4: write_super\n"));
sb->s_dirt = 0;
}
static void qnx4_put_inode(struct inode *inode)
{
if (inode->i_nlink != 0) {
return;
}
inode->i_size = 0;
}
static void qnx4_write_inode(struct inode *inode)
{
struct qnx4_inode_entry *raw_inode;
int block, ino;
struct buffer_head *bh;
ino = inode->i_ino;
QNX4DEBUG(("qnx4: write inode 1.\n"));
if (inode->i_nlink == 0) {
return;
}
if (!ino) {
printk("qnx4: bad inode number on dev %s: %d is out of range\n",
kdevname(inode->i_dev), ino);
return;
}
QNX4DEBUG(("qnx4: write inode 2.\n"));
block = ino / QNX4_INODES_PER_BLOCK;
if (!(bh = bread(inode->i_dev, block, QNX4_BLOCK_SIZE))) {
printk("qnx4: major problem: unable to read inode from dev "
"%s\n", kdevname(inode->i_dev));
return;
}
raw_inode = ((struct qnx4_inode_entry *) bh->b_data) +
(ino % QNX4_INODES_PER_BLOCK);
raw_inode->di_mode = inode->i_mode;
raw_inode->di_uid = inode->i_uid;
raw_inode->di_gid = inode->i_gid;
raw_inode->di_nlink = inode->i_nlink;
raw_inode->di_size = inode->i_size;
raw_inode->di_mtime = inode->i_mtime;
raw_inode->di_atime = inode->i_atime;
raw_inode->di_ctime = inode->i_ctime;
raw_inode->di_first_xtnt.xtnt_size = inode->i_blocks;
mark_buffer_dirty(bh, 1);
brelse(bh);
}
#endif
static struct super_block *qnx4_read_super(struct super_block *, void *, int);
static void qnx4_put_super(struct super_block *sb);
static void qnx4_read_inode(struct inode *);
static int qnx4_remount(struct super_block *sb, int *flags, char *data);
static int qnx4_statfs(struct super_block *, struct statfs *, int);
static struct super_operations qnx4_sops =
{
qnx4_read_inode,
#ifdef CONFIG_QNX4FS_RW
qnx4_write_inode,
#else
NULL,
#endif
qnx4_put_inode,
#ifdef CONFIG_QNX4FS_RW
qnx4_delete_inode,
NULL, /* notify_change */
#else
NULL, /* delete_inode */
NULL, /* notify_change */
#endif
qnx4_put_super,
#ifdef CONFIG_QNX4FS_RW
qnx4_write_super,
#else
NULL,
#endif
qnx4_statfs,
qnx4_remount,
NULL /* clear_inode */
};
static int qnx4_remount(struct super_block *sb, int *flags, char *data)
{
struct qnx4_sb_info *qs;
qs = &sb->u.qnx4_sb;
qs->Version = QNX4_VERSION;
if (*flags & MS_RDONLY) {
return 0;
}
mark_buffer_dirty(qs->sb_buf, 1);
return 0;
}
struct buffer_head *inode_getblk(struct inode *inode, int nr,
int create)
{
int tmp;
int tst;
struct buffer_head *result = NULL;
tst = nr;
repeat:
tmp = tst;
if (tmp) {
result = getblk(inode->i_dev, tmp, QNX4_BLOCK_SIZE);
if (tmp == tst) {
return result;
}
brelse(result);
goto repeat;
}
if (!create) {
return NULL;
}
#if 0
tmp = qnx4_new_block(inode->i_sb);
if (!tmp) {
return NULL;
}
result = getblk(inode->i_dev, tmp, QNX4_BLOCK_SIZE);
if (tst) {
qnx4_free_block(inode->i_sb, tmp);
brelse(result);
goto repeat;
}
tst = tmp;
#endif
inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode);
return result;
}
struct buffer_head *qnx4_bread(struct inode *inode, int block, int create)
{
struct buffer_head *bh;
bh = inode_getblk(inode, block, create);
if (!bh || buffer_uptodate(bh)) {
return bh;
}
ll_rw_block(READ, 1, &bh);
wait_on_buffer(bh);
if (buffer_uptodate(bh)) {
return bh;
}
brelse(bh);
return NULL;
}
static int qnx4_statfs(struct super_block *sb,
struct statfs *buf, int bufsize)
{
struct statfs tmp;
memset(&tmp, 0, sizeof tmp);
tmp.f_type = sb->s_magic;
tmp.f_bsize = sb->s_blocksize;
tmp.f_blocks = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_size) * 8;
tmp.f_bfree = qnx4_count_free_blocks(sb);
tmp.f_bavail = tmp.f_bfree;
tmp.f_files = 0x00; /* change this !!! */
tmp.f_ffree = qnx4_count_free_inodes(sb);
tmp.f_namelen = QNX4_NAME_MAX;
return copy_to_user(buf, &tmp, bufsize) ? -EFAULT : 0;
}
/*
* Check the root directory of the filesystem to make sure
* it really _is_ a qnx4 filesystem, and to check the size
* of the directory entry.
*/
static const char *qnx4_checkroot(struct super_block *s)
{
struct buffer_head *bh;
struct qnx4_inode_entry *rootdir;
int rd, rl;
int i, j;
int found = 0;
if (s == NULL) {
return "no qnx4 filesystem (null superblock).";
}
if (*(s->u.qnx4_sb.sb->RootDir.di_fname) != '/') {
return "no qnx4 filesystem (no root dir).";
} else {
QNX4DEBUG(("QNX4 filesystem found on dev %s.\n", kdevname(s->s_dev)));
rd = s->u.qnx4_sb.sb->RootDir.di_first_xtnt.xtnt_blk - 1;
rl = s->u.qnx4_sb.sb->RootDir.di_first_xtnt.xtnt_size;
for (j = 0; j < rl; j++) {
bh = bread(s->s_dev, rd + j, QNX4_BLOCK_SIZE); /* root dir, first block */
if (bh == NULL) {
return "unable to read root entry.";
}
for (i = 0; i < QNX4_INODES_PER_BLOCK; i++) {
rootdir = (struct qnx4_inode_entry *) (bh->b_data + i * QNX4_DIR_ENTRY_SIZE);
if (rootdir->di_fname != NULL) {
QNX4DEBUG(("Rootdir entry found : [%s]\n", rootdir->di_fname));
if (!strncmp(rootdir->di_fname, QNX4_BMNAME, sizeof QNX4_BMNAME)) {
found = 1;
s->u.qnx4_sb.BitMap = rootdir; /* keep bitmap inode known */
break;
}
}
}
brelse(bh);
if (found != 0) {
break;
}
}
if (found == 0) {
return "bitmap file not found.";
}
}
return NULL;
}
static struct super_block *qnx4_read_super(struct super_block *s,
void *data, int silent)
{
struct buffer_head *bh;
kdev_t dev = s->s_dev;
#if CHECK_BOOT_SIGNATURE
char *tmpc;
#endif
const char *errmsg;
MOD_INC_USE_COUNT;
lock_super(s);
set_blocksize(dev, QNX4_BLOCK_SIZE);
s->s_blocksize = QNX4_BLOCK_SIZE;
s->s_blocksize_bits = 9;
s->s_dev = dev;
#if CHECK_BOOT_SIGNATURE
bh = bread(dev, 0, QNX4_BLOCK_SIZE);
if (!bh) {
printk("qnx4: unable to read the boot sector\n");
goto outnobh;
}
tmpc = (char *) bh->b_data;
if (tmpc[4] != 'Q' || tmpc[5] != 'N' || tmpc[6] != 'X' ||
tmpc[7] != '4' || tmpc[8] != 'F' || tmpc[9] != 'S') {
printk("qnx4: wrong fsid in boot sector.\n");
goto out;
}
brelse(bh);
#endif
bh = bread(dev, 1, QNX4_BLOCK_SIZE);
if (!bh) {
printk("qnx4: unable to read the superblock\n");
goto outnobh;
}
s->s_op = &qnx4_sops;
s->s_magic = QNX4_SUPER_MAGIC;
#ifndef CONFIG_QNX4FS_RW
s->s_flags |= MS_RDONLY; /* Yup, read-only yet */
#endif
s->u.qnx4_sb.sb_buf = bh;
s->u.qnx4_sb.sb = (struct qnx4_super_block *) bh->b_data;
s->s_root =
d_alloc_root(iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK), NULL);
if (s->s_root == NULL) {
printk("qnx4: get inode failed\n");
goto out;
}
errmsg = qnx4_checkroot(s);
if (errmsg != NULL) {
printk("qnx4: %s\n", errmsg);
goto out;
}
brelse(bh);
unlock_super(s);
s->s_dirt = 1;
return s;
out:
brelse(bh);
outnobh:
s->s_dev = 0;
unlock_super(s);
MOD_DEC_USE_COUNT;
return NULL;
}
static void qnx4_put_super(struct super_block *sb)
{
MOD_DEC_USE_COUNT;
return;
}
static void qnx4_read_inode(struct inode *inode)
{
struct buffer_head *bh;
struct qnx4_inode_entry *raw_inode;
int block, ino;
ino = inode->i_ino;
inode->i_op = NULL;
inode->i_mode = 0;
QNX4DEBUG(("Reading inode : [%d]\n", ino));
if (!ino) {
printk("qnx4: bad inode number on dev %s: %d is out of range\n",
kdevname(inode->i_dev), ino);
return;
}
block = ino / QNX4_INODES_PER_BLOCK;
if (!(bh = bread(inode->i_dev, block, QNX4_BLOCK_SIZE))) {
printk("qnx4: major problem: unable to read inode from dev "
"%s\n", kdevname(inode->i_dev));
return;
}
raw_inode = ((struct qnx4_inode_entry *) bh->b_data) +
(ino % QNX4_INODES_PER_BLOCK);
inode->i_mode = raw_inode->di_mode;
inode->i_uid = raw_inode->di_uid;
inode->i_gid = raw_inode->di_gid;
inode->i_nlink = raw_inode->di_nlink;
inode->i_size = raw_inode->di_size;
inode->i_mtime = raw_inode->di_mtime;
inode->i_atime = raw_inode->di_atime;
inode->i_ctime = raw_inode->di_ctime;
inode->i_blocks = raw_inode->di_first_xtnt.xtnt_size;
inode->i_blksize = QNX4_DIR_ENTRY_SIZE;
memcpy(&inode->u.qnx4_i, (struct qnx4_inode_info *) raw_inode, QNX4_DIR_ENTRY_SIZE);
inode->i_op = &qnx4_file_inode_operations;
if (S_ISREG(inode->i_mode)) {
inode->i_op = &qnx4_file_inode_operations;
} else {
if (S_ISDIR(inode->i_mode)) {
inode->i_op = &qnx4_dir_inode_operations;
} else {
if (S_ISLNK(inode->i_mode)) {
inode->i_op = &qnx4_symlink_inode_operations;
} else {
if (S_ISCHR(inode->i_mode)) {
inode->i_op = &chrdev_inode_operations;
} else {
if (S_ISBLK(inode->i_mode)) {
inode->i_op = &blkdev_inode_operations;
} else {
if (S_ISFIFO(inode->i_mode)) {
init_fifo(inode);
}
}
}
}
}
}
brelse(bh);
}
static struct file_system_type qnx4_fs_type =
{
"qnx4",
FS_REQUIRES_DEV,
qnx4_read_super,
NULL
};
__initfunc(int init_qnx4_fs(void))
{
printk("QNX4 filesystem v0.2 registered.\n");
return register_filesystem(&qnx4_fs_type);
}
#ifdef MODULE
EXPORT_NO_SYMBOLS;
int init_module(void)
{
return init_qnx4_fs();
}
void cleanup_module(void)
{
unregister_filesystem(&qnx4_fs_type);
}
#endif
/*
* QNX4 file system, Linux implementation.
*
* Version : 0.1
*
* Using parts of the xiafs filesystem.
*
* History :
*
* 01-06-1998 by Richard Frowijn : first release.
* 21-06-1998 by Frank Denis : dcache support, fixed error codes.
* 04-07-1998 by Frank Denis : first step for rmdir/unlink.
*/
#include <linux/sched.h>
#include <linux/qnx4_fs.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <linux/errno.h>
#include <asm/segment.h>
/*
* check if the filename is correct. For some obscure reason, qnx writes a
* new file twice in the directory entry, first with all possible options at 0
* and for a second time the way it is, they want us not to access the qnx
* filesystem when whe are using linux.
*/
static int qnx4_match(int len, const char *name,
struct buffer_head *bh, unsigned long *offset)
{
struct qnx4_inode_entry *de;
int namelen;
if (bh == NULL) {
printk("qnx4: matching unassigned buffer !\n");
return 0;
}
de = (struct qnx4_inode_entry *) (bh->b_data + *offset);
*offset += QNX4_DIR_ENTRY_SIZE;
if ((de->di_status & 0x08) == 0x08) {
namelen = QNX4_NAME_MAX;
} else {
namelen = _SHORT_NAME_MAX;
}
/* "" means "." ---> so paths like "/usr/lib//libc.a" work */
if (!len && (de->di_fname[0] == '.') && (de->di_fname[1] == '\0')) {
return 1;
}
if (len != strlen(de->di_fname)) {
return 0;
}
if (strncmp(name, de->di_fname, len) == 0) {
if ((de->di_mode) || (de->di_status == QNX4_FILE_LINK)) {
if (de->di_status) {
return 1;
}
}
}
return 0;
}
static struct buffer_head *qnx4_find_entry(int len, struct inode *dir,
const char *name, struct qnx4_inode_entry **res_dir, int *ino)
{
unsigned long block, offset, blkofs;
struct buffer_head *bh;
*res_dir = NULL;
if (!dir || !dir->i_sb) {
if (!dir) {
printk("qnx4: NULL dir.\n");
} else {
printk("qnx4: no superblock on dir.\n");
}
return NULL;
}
bh = NULL;
blkofs = dir->u.qnx4_i.i_first_xtnt.xtnt_blk - 1;
offset = block = 0;
while (block * QNX4_BLOCK_SIZE + offset < dir->i_size) {
if (!bh) {
bh = qnx4_bread(dir, block + blkofs, 0);
if (!bh) {
block++;
continue;
}
}
*res_dir = (struct qnx4_inode_entry *) (bh->b_data + offset);
if (qnx4_match(len, name, bh, &offset)) {
*ino = (block + blkofs) * QNX4_INODES_PER_BLOCK +
(offset / QNX4_DIR_ENTRY_SIZE) - 1;
return bh;
}
if (offset < bh->b_size) {
continue;
}
brelse(bh);
bh = NULL;
offset = 0;
block++;
}
brelse(bh);
*res_dir = NULL;
return NULL;
}
int qnx4_lookup(struct inode *dir, struct dentry *dentry)
{
int ino;
struct qnx4_inode_entry *de;
struct qnx4_link_info *lnk;
struct buffer_head *bh;
const char *name = dentry->d_name.name;
int len = dentry->d_name.len;
struct inode *foundinode;
if (!dir) {
return -EBADF;
}
if (!S_ISDIR(dir->i_mode)) {
return -EBADF;
}
if (!(bh = qnx4_find_entry(len, dir, name, &de, &ino))) {
return -ENOENT;
}
/* The entry is linked, let's get the real info */
if ((de->di_status & QNX4_FILE_LINK) == QNX4_FILE_LINK) {
lnk = (struct qnx4_link_info *) de;
ino = (lnk->dl_inode_blk - 1) * QNX4_INODES_PER_BLOCK +
lnk->dl_inode_ndx;
}
brelse(bh);
if ((foundinode = iget(dir->i_sb, ino)) == NULL) {
QNX4DEBUG(("qnx4: lookup->iget -> NULL\n"));
return -EACCES;
}
d_add(dentry, foundinode);
return 0;
}
#ifdef CONFIG_QNX4FS_RW
int qnx4_create(struct inode *dir, struct dentry *dentry, int mode)
{
QNX4DEBUG(("qnx4: qnx4_create\n"));
if (dir == NULL) {
return -ENOENT;
}
return -ENOSPC;
}
int qnx4_rmdir(struct inode *dir, struct dentry *dentry)
{
struct buffer_head *bh;
struct qnx4_inode_entry *de;
struct inode *inode;
int retval;
int ino;
QNX4DEBUG(("qnx4: qnx4_rmdir [%s]\n", dentry->d_name.name));
bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name,
&de, &ino);
if (bh == NULL) {
return -ENOENT;
}
if ((inode = iget(dir->i_sb, ino)) == NULL) {
QNX4DEBUG(("qnx4: lookup->iget -> NULL\n"));
retval = -EACCES;
goto end_rmdir;
}
retval = -EPERM;
if ((dir->i_mode & S_ISVTX) &&
current->fsuid != inode->i_uid &&
current->fsuid != dir->i_uid && !capable(CAP_FOWNER)) {
QNX4DEBUG(("qnx4: rmdir->capabilities\n"));
goto end_rmdir;
}
if (inode->i_dev != dir->i_dev) {
QNX4DEBUG(("qnx4: rmdir->different devices\n"));
goto end_rmdir;
}
if (inode == dir) { /* we may not delete ".", but "../dir" is ok */
QNX4DEBUG(("qnx4: inode==dir\n"));
goto end_rmdir;
}
if (!S_ISDIR(inode->i_mode)) {
QNX4DEBUG(("qnx4: rmdir->not a directory\n"));
retval = -ENOTDIR;
goto end_rmdir;
}
#if 0
if (!empty_dir(inode)) {
retval = -ENOTEMPTY;
goto end_rmdir;
}
#endif
if (dentry->d_count > 1) {
retval = -EBUSY;
goto end_rmdir;
}
if (inode->i_nlink != 2) {
QNX4DEBUG(("empty directory has nlink!=2 (%d)\n", inode->i_nlink));
}
QNX4DEBUG(("qnx4: deleting directory\n"));
de->di_status = 0;
memset(de->di_fname, 0, sizeof de->di_fname);
de->di_mode = 0;
mark_buffer_dirty(bh, 1);
inode->i_nlink = 0;
mark_inode_dirty(inode);
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->i_nlink--;
mark_inode_dirty(dir);
d_delete(dentry);
retval = 0;
end_rmdir:
brelse(bh);
return retval;
}
int qnx4_unlink(struct inode *dir, struct dentry *dentry)
{
struct buffer_head *bh;
struct qnx4_inode_entry *de;
struct inode *inode;
int retval;
int ino;
QNX4DEBUG(("qnx4: qnx4_unlink [%s]\n", dentry->d_name.name));
bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name,
&de, &ino);
if (bh == NULL) {
return -ENOENT;
}
if ((inode = iget(dir->i_sb, ino)) == NULL) {
QNX4DEBUG(("qnx4: lookup->iget -> NULL\n"));
retval = -EACCES;
goto end_unlink;
}
retval = -EPERM;
if (S_ISDIR(inode->i_mode)) {
goto end_unlink;
}
if ((dir->i_mode & S_ISVTX) &&
current->fsuid != inode->i_uid &&
current->fsuid != dir->i_uid && !capable(CAP_FOWNER)) {
goto end_unlink;
}
if (!inode->i_nlink) {
QNX4DEBUG(("Deleting nonexistent file (%s:%lu), %d\n",
kdevname(inode->i_dev),
inode->i_ino, inode->i_nlink));
inode->i_nlink = 1;
}
de->di_status = 0;
memset(de->di_fname, 0, sizeof de->di_fname);
de->di_mode = 0;
mark_buffer_dirty(bh, 1);
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
mark_inode_dirty(dir);
inode->i_nlink--;
inode->i_ctime = dir->i_ctime;
mark_inode_dirty(inode);
d_delete(dentry);
retval = 0;
end_unlink:
brelse(bh);
return retval;
}
#endif
/*
* QNX4 file system, Linux implementation.
*
* Version : 0.1
*
* Using parts of the xiafs filesystem.
*
* History :
*
* 28-05-1998 by Richard Frowijn : first release.
* 21-06-1998 by Frank Denis : ugly changes to make it compile on Linux 2.1.99+
*/
/* THIS FILE HAS TO BE REWRITTEN */
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/qnx4_fs.h>
#include <linux/stat.h>
#include <asm/segment.h>
#include <asm/uaccess.h>
static int qnx4_readlink(struct dentry *, char *, int);
static struct dentry *qnx4_follow_link(struct dentry *, struct dentry *);
/*
* symlinks can't do much...
*/
struct inode_operations qnx4_symlink_inode_operations =
{
NULL, /* no file-operations */
#ifdef CONFIG_QNX4FS_RW
qnx4_create, /* create */
#else
NULL,
#endif
NULL, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
qnx4_readlink, /* readlink */
qnx4_follow_link, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
static struct dentry *qnx4_follow_link(struct dentry *dentry,
struct dentry *base)
{
#if 0
struct inode *inode = dentry->d_inode;
struct buffer_head *bh;
if (!inode) {
return ERR_PTR(-ENOENT);
}
if (current->link_count > 5) {
return ERR_PTR(-ELOOP);
}
if (!(bh = qnx4_bread(inode, 0, 0))) {
return ERR_PTR(-EIO);
}
current->link_count++;
current->link_count--;
brelse(bh);
return 0;
#else
printk("qnx4: qnx4_follow_link needs to be fixed.\n");
return ERR_PTR(-EIO);
#endif
}
static int qnx4_readlink(struct dentry *dentry, char *buffer, int buflen)
{
struct inode *inode = dentry->d_inode;
struct buffer_head *bh;
int i;
char c;
struct qnx4_inode_info *qnx4_ino;
QNX4DEBUG(("qnx4: qnx4_readlink() called\n"));
if (buffer == NULL || inode == NULL || !S_ISLNK(inode->i_mode)) {
return -EINVAL;
}
qnx4_ino = &inode->u.qnx4_i;
if (buflen > 1023) {
buflen = 1023;
}
bh = bread(inode->i_dev, qnx4_ino->i_first_xtnt.xtnt_blk,
QNX4_BLOCK_SIZE);
QNX4DEBUG(("qnx4: qnx4_bread sym called -> [%s]\n",
bh->b_data));
if (bh == NULL) {
QNX4DEBUG(("qnx4: NULL symlink bh\n"));
return 0;
}
if (bh->b_data[0] != 0) {
i = 0;
while (i < buflen && (c = bh->b_data[i])) {
i++;
put_user(c, buffer++);
}
brelse(bh);
return i;
} else {
brelse(bh);
memcpy(buffer, "fixme", 5);
return 5;
}
}
/*
* QNX4 file system, Linux implementation.
*
* Version : 0.1
*
* Using parts of the xiafs filesystem.
*
* History :
*
* 30-06-1998 by Frank DENIS : ugly filler.
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/malloc.h>
#include <linux/qnx4_fs.h>
#include <linux/fs.h>
#include <linux/locks.h>
#include <asm/uaccess.h>
#ifdef CONFIG_QNX4FS_RW
void qnx4_truncate(struct inode *inode)
{
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
S_ISLNK(inode->i_mode))) {
return;
}
if (!(S_ISDIR(inode->i_mode))) {
/* TODO */
}
QNX4DEBUG(("qnx4: qnx4_truncate called\n"));
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode);
}
#endif
......@@ -288,7 +288,7 @@ asmlinkage ssize_t sys_readv(unsigned long fd, const struct iovec * vector,
file = fget(fd);
if (!file)
goto bad_file;
if (file->f_mode & FMODE_READ)
if (file->f_op && file->f_op->read && (file->f_mode & FMODE_READ))
ret = do_readv_writev(VERIFY_WRITE, file, vector, count);
fput(file);
......@@ -309,7 +309,7 @@ asmlinkage ssize_t sys_writev(unsigned long fd, const struct iovec * vector,
file = fget(fd);
if (!file)
goto bad_file;
if (file->f_mode & FMODE_WRITE) {
if (file->f_op && file->f_op->write && (file->f_mode & FMODE_WRITE)) {
down(&file->f_dentry->d_inode->i_sem);
ret = do_readv_writev(VERIFY_READ, file, vector, count);
up(&file->f_dentry->d_inode->i_sem);
......
......@@ -195,4 +195,22 @@ static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
return csum_fold(sum);
}
/*
* Copy and checksum to user
*/
#define HAVE_CSUM_COPY_USER
static __inline__ unsigned int csum_and_copy_to_user (const char *src, char *dst,
int len, int sum, int *err_ptr)
{
int *src_err_ptr=NULL;
if (verify_area(VERIFY_WRITE, dst, len) == 0)
return csum_partial_copy_generic(src, dst, len, sum, src_err_ptr, err_ptr);
if (len)
*err_ptr = -EFAULT;
return sum;
}
#endif
......@@ -272,6 +272,7 @@ static inline int buffer_protected(struct buffer_head * bh)
#include <linux/smb_fs_i.h>
#include <linux/hfs_fs_i.h>
#include <linux/adfs_fs_i.h>
#include <linux/qnx4_fs_i.h>
/*
* Attribute flags. These should be or-ed together to figure out what
......@@ -381,6 +382,7 @@ struct inode {
struct smb_inode_info smbfs_i;
struct hfs_inode_info hfs_i;
struct adfs_inode_info adfs_i;
struct qnx4_inode_info qnx4_i;
struct socket socket_i;
void *generic_ip;
} u;
......@@ -534,6 +536,7 @@ extern int fasync_helper(int, struct file *, int, struct fasync_struct **);
#include <linux/smb_fs_sb.h>
#include <linux/hfs_fs_sb.h>
#include <linux/adfs_fs_sb.h>
#include <linux/qnx4_fs_sb.h>
extern struct list_head super_blocks;
......@@ -575,6 +578,7 @@ struct super_block {
struct smb_sb_info smbfs_sb;
struct hfs_sb_info hfs_sb;
struct adfs_sb_info adfs_sb;
struct qnx4_sb_info qnx4_sb;
void *generic_sbp;
} u;
};
......
......@@ -49,7 +49,7 @@ struct partition {
unsigned char end_cyl; /* end cylinder */
unsigned int start_sect; /* starting sector counting from 0 */
unsigned int nr_sects; /* nr of sectors in partition */
};
} __attribute__((packed));
struct hd_struct {
long start_sect;
......
......@@ -110,9 +110,9 @@ void nfsd_fh_free(void);
static __inline__ struct svc_fh *
fh_copy(struct svc_fh *dst, struct svc_fh *src)
{
if (src->fh_dverified) {
if (src->fh_dverified || src->fh_locked) {
struct dentry *dentry = src->fh_dentry;
printk("fh_copy: copying %s/%s, already verified!\n",
printk(KERN_ERR "fh_copy: copying %s/%s, already verified!\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
}
......@@ -133,18 +133,24 @@ fh_init(struct svc_fh *fhp)
static inline void
fh_lock(struct svc_fh *fhp)
{
struct inode *inode = fhp->fh_dentry->d_inode;
struct dentry *dentry = fhp->fh_dentry;
struct inode *inode;
/*
dfprintk(FILEOP, "nfsd: fh_lock(%x/%ld) locked = %d\n",
SVCFH_DEV(fhp), SVCFH_INO(fhp), fhp->fh_locked);
*/
if (!fhp->fh_dverified) {
printk(KERN_ERR "fh_lock: fh not verified!\n");
return;
}
if (fhp->fh_locked) {
printk(KERN_WARNING "fh_lock: %s/%s already locked!\n",
fhp->fh_dentry->d_parent->d_name.name,
fhp->fh_dentry->d_name.name);
dentry->d_parent->d_name.name, dentry->d_name.name);
return;
}
inode = dentry->d_inode;
down(&inode->i_sem);
if (!fhp->fh_pre_mtime)
fhp->fh_pre_mtime = inode->i_mtime;
......@@ -157,8 +163,13 @@ fh_lock(struct svc_fh *fhp)
static inline void
fh_unlock(struct svc_fh *fhp)
{
if (!fhp->fh_dverified)
printk(KERN_ERR "fh_unlock: fh not verified!\n");
if (fhp->fh_locked) {
struct inode *inode = fhp->fh_dentry->d_inode;
struct dentry *dentry = fhp->fh_dentry;
struct inode *inode = dentry->d_inode;
if (!fhp->fh_post_version)
fhp->fh_post_version = inode->i_version;
fhp->fh_locked = 0;
......
/*
* Name : qnx4_fs.h
* Author : Richard Frowijn
* Function : qnx4 global filesystem definitions
* Version : 1.0
* Last modified : 23-03-1998
*
* History : 23-03-1998 created
*/
#ifndef _LINUX_QNX4_FS_H
#define _LINUX_QNX4_FS_H
#include <linux/qnxtypes.h>
#define QNX4_ROOT_INO 1
#define _MAX_XTNTS_PER_XBLK 60
/* for di_status */
#define QNX4_FILE_USED 0x01
#define QNX4_FILE_MODIFIED 0x02
#define QNX4_FILE_BUSY 0x04
#define QNX4_FILE_LINK 0x08
#define QNX4_FILE_INODE 0x10
#define QNX4_FILE_FSYSCLEAN 0x20
#define QNX4_I_MAP_SLOTS 8
#define QNX4_Z_MAP_SLOTS 64
#define QNX4_SUPER_MAGIC 0x002f /* qnx4 fs detection */
#define QNX4_VALID_FS 0x0001 /* Clean fs. */
#define QNX4_ERROR_FS 0x0002 /* fs has errors. */
#define QNX4_BLOCK_SIZE 0x200 /* blocksize of 512 bytes */
#define QNX4_DIR_ENTRY_SIZE 0x040 /* dir entry size */
#define QNX4_XBLK_ENTRY_SIZE 0x200 /* xblk entry size */
#define QNX4_INODES_PER_BLOCK 0x08 /* 512 / 64 */
/* for filenames */
#define _SHORT_NAME_MAX 16
#define QNX4_NAME_MAX 48
/*
* This is the original qnx4 inode layout on disk.
*/
struct qnx4_inode_entry {
char di_fname[16];
off_t di_size;
_xtnt_t di_first_xtnt;
long di_xblk;
time_t di_ftime;
time_t di_mtime;
time_t di_atime;
time_t di_ctime;
_nxtnt_t di_num_xtnts;
mode_t di_mode;
muid_t di_uid;
mgid_t di_gid;
nlink_t di_nlink;
char di_zero[4];
_ftype_t di_type;
unsigned char di_status;
};
struct qnx4_link_info {
char dl_fname[QNX4_NAME_MAX];
long dl_inode_blk;
unsigned char dl_inode_ndx;
unsigned char dl_spare[10];
unsigned char dl_status;
};
struct qnx4_xblk {
long xblk_next_xblk;
long xblk_prev_xblk;
unsigned char xblk_num_xtnts;
char xblk_spare[3];
long xblk_num_blocks;
_xtnt_t xblk_xnts[_MAX_XTNTS_PER_XBLK];
char xblk_signature[8];
_xtnt_t xblk_first_xtnt;
};
struct qnx4_super_block {
struct qnx4_inode_entry RootDir;
struct qnx4_inode_entry Inode;
struct qnx4_inode_entry Boot;
struct qnx4_inode_entry AltBoot;
};
#ifdef __KERNEL__
#define QNX4_DEBUG 0
#if QNX4_DEBUG
#define QNX4DEBUG(X) printk X
#else
#define QNX4DEBUG(X) (void) 0
#endif
extern int qnx4_lookup(struct inode *dir, struct dentry *dentry);
extern unsigned long qnx4_count_free_inodes(struct super_block *sb);
extern unsigned long qnx4_count_free_blocks(struct super_block *sb);
extern struct buffer_head *qnx4_getblk(struct inode *, int, int);
extern struct buffer_head *qnx4_bread(struct inode *, int, int);
extern int init_qnx4_fs(void);
extern int qnx4_create(struct inode *dir, struct dentry *dentry, int mode);
extern struct inode_operations qnx4_file_inode_operations;
extern struct inode_operations qnx4_dir_inode_operations;
extern struct inode_operations qnx4_symlink_inode_operations;
extern int qnx4_is_free(struct super_block *sb, int block);
extern int qnx4_set_bitmap(struct super_block *sb, int block, int busy);
extern int qnx4_create(struct inode *inode, struct dentry *dentry, int mode);
extern void qnx4_truncate(struct inode *inode);
extern void qnx4_free_inode(struct inode *inode);
extern int qnx4_unlink(struct inode *dir, struct dentry *dentry);
extern int qnx4_rmdir(struct inode *dir, struct dentry *dentry);
extern int qnx4_sync_file(struct file *file, struct dentry *dentry);
extern int qnx4_sync_inode(struct inode *inode);
extern int qnx4_bmap(struct inode *inode, int block);
#endif /* __KERNEL__ */
#endif
/*
* Name : qnx4_fs_i.h
* Author : Richard Frowijn
* Function : qnx4 inode definitions
* Version : 1.0
* Last modified : 25-05-1998
*
* History : 23-03-1998 created
*
*/
#ifndef _QNX4_FS_I
#define _QNX4_FS_I
#include <linux/qnxtypes.h>
/*
* qnx4 fs inode entry
*/
struct qnx4_inode_info {
char i_reserved[16]; /* 16 */
off_t i_size; /* 4 */
_xtnt_t i_first_xtnt; /* 8 */
long i_xblk; /* 4 */
time_t i_ftime; /* 4 */
time_t i_mtime; /* 4 */
time_t i_atime; /* 4 */
time_t i_ctime; /* 4 */
_nxtnt_t i_num_xtnts; /* 2 */
mode_t i_mode; /* 2 */
muid_t i_uid; /* 2 */
mgid_t i_gid; /* 2 */
nlink_t i_nlink; /* 2 */
char i_zero[4]; /* 4 */
_ftype_t i_type; /* 1 */
unsigned char i_status; /* 1 */
};
#endif
/*
* Name : qnx4_fs_sb.h
* Author : Richard Frowijn
* Function : qnx4 superblock definitions
* Version : 1.0
* Last modified : 20-05-1998
*
* History : 23-03-1998 created
*
*/
#ifndef _QNX4_FS_SB
#define _QNX4_FS_SB
#include <linux/qnxtypes.h>
/*
* qnx4 super-block data in memory
*/
struct qnx4_sb_info {
struct buffer_head *sb_buf; /* superblock buffer */
struct qnx4_super_block *sb; /* our superblock */
unsigned int Version; /* may be useful */
struct qnx4_inode_entry *BitMap; /* useful */
};
#endif
/*
* Name : qnxtypes.h
* Author : Richard Frowijn
* Function : standard qnx types
* Version : 1.0
* Last modified : 22-03-1998
*
* History : 22-03-1998 created
*
*/
#ifndef _QNX4TYPES_H
#define _QNX4TYPES_H
typedef unsigned short _nxtnt_t;
typedef unsigned char _ftype_t;
typedef struct {
long xtnt_blk;
long xtnt_size;
} _xtnt_t;
typedef unsigned short muid_t;
typedef unsigned short mgid_t;
typedef unsigned long qnx_off_t;
typedef unsigned short qnx_nlink_t;
#endif
......@@ -75,6 +75,7 @@ extern int try_to_free_pages(unsigned int gfp_mask, int count);
/* linux/mm/page_io.c */
extern void rw_swap_page(int, unsigned long, char *, int);
extern void rw_swap_page_nocache(int, unsigned long, char *);
extern void swap_after_unlock_page (unsigned long entry);
/* linux/mm/page_alloc.c */
extern void swap_in(struct task_struct *, struct vm_area_struct *,
......@@ -84,10 +85,15 @@ extern void swap_in(struct task_struct *, struct vm_area_struct *,
/* linux/mm/swap_state.c */
extern void show_swap_cache_info(void);
extern int add_to_swap_cache(struct page *, unsigned long);
extern void swap_duplicate(unsigned long);
extern void swap_after_unlock_page (unsigned long entry);
extern int swap_duplicate(unsigned long);
extern int swap_check_entry(unsigned long);
extern struct page * read_swap_cache_async(unsigned long, int);
#define read_swap_cache(entry) read_swap_cache_async(entry, 1);
/*
* Make these inline later once they are working properly.
*/
extern void delete_from_swap_cache(struct page *page);
extern void free_page_and_swap_cache(unsigned long addr);
/* linux/mm/swapfile.c */
extern unsigned int nr_swapfiles;
......@@ -100,6 +106,8 @@ struct swap_list_t {
int next; /* swapfile to be used next */
};
extern struct swap_list_t swap_list;
int sys_swapoff(const char *);
int sys_swapon(const char *, int);
/*
* vm_ops not present page codes for shared memory.
......@@ -148,14 +156,6 @@ static inline int is_page_shared(struct page *page)
return (count > 1);
}
/*
* Make these inline later once they are working properly.
*/
extern long find_in_swap_cache(struct page *page);
extern int delete_from_swap_cache(struct page *page);
extern void remove_from_swap_cache(struct page *page);
extern void free_page_and_swap_cache(unsigned long addr);
#endif /* __KERNEL__*/
#endif /* _LINUX_SWAP_H */
......@@ -125,4 +125,8 @@ asmlinkage int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
return -ENOSYS;
}
void shm_unuse(unsigned long entry, unsigned long page)
{
}
#endif /* CONFIG_SYSVIPC */
......@@ -24,14 +24,6 @@
#include <asm/bitops.h>
#include <asm/pgtable.h>
#ifdef SWAP_CACHE_INFO
unsigned long swap_cache_add_total = 0;
unsigned long swap_cache_add_success = 0;
unsigned long swap_cache_del_total = 0;
unsigned long swap_cache_del_success = 0;
unsigned long swap_cache_find_total = 0;
unsigned long swap_cache_find_success = 0;
/*
* Keep a reserved false inode which we will use to mark pages in the
* page cache are acting as swap cache instead of file cache.
......@@ -43,6 +35,13 @@ unsigned long swap_cache_find_success = 0;
*/
struct inode swapper_inode;
#ifdef SWAP_CACHE_INFO
unsigned long swap_cache_add_total = 0;
unsigned long swap_cache_add_success = 0;
unsigned long swap_cache_del_total = 0;
unsigned long swap_cache_del_success = 0;
unsigned long swap_cache_find_total = 0;
unsigned long swap_cache_find_success = 0;
void show_swap_cache_info(void)
{
......@@ -63,13 +62,13 @@ int add_to_swap_cache(struct page *page, unsigned long entry)
page_address(page), atomic_read(&page->count), entry);
#endif
if (PageTestandSetSwapCache(page)) {
printk("swap_cache: replacing non-empty entry %08lx "
printk(KERN_ERR "swap_cache: replacing non-empty entry %08lx "
"on page %08lx\n",
page->offset, page_address(page));
return 0;
}
if (page->inode) {
printk("swap_cache: replacing page-cached entry "
printk(KERN_ERR "swap_cache: replacing page-cached entry "
"on page %08lx\n", page_address(page));
return 0;
}
......@@ -85,12 +84,16 @@ int add_to_swap_cache(struct page *page, unsigned long entry)
}
/*
* If swap_map[] reaches SWAP_MAP_MAX the entries are treated as "permanent".
* Verify that a swap entry is valid and increment its swap map count.
*
* Note: if swap_map[] reaches SWAP_MAP_MAX the entries are treated as
* "permanent", but will be reclaimed by the next swapoff.
*/
void swap_duplicate(unsigned long entry)
int swap_duplicate(unsigned long entry)
{
struct swap_info_struct * p;
unsigned long offset, type;
int result = 0;
if (!entry)
goto out;
......@@ -105,36 +108,44 @@ void swap_duplicate(unsigned long entry)
goto bad_offset;
if (!p->swap_map[offset])
goto bad_unused;
/*
* Entry is valid, so increment the map count.
*/
if (p->swap_map[offset] < SWAP_MAP_MAX)
p->swap_map[offset]++;
else {
static int overflow = 0;
if (overflow++ < 5)
printk("swap_duplicate: entry %08lx map count=%d\n",
printk(KERN_WARNING
"swap_duplicate: entry %08lx map count=%d\n",
entry, p->swap_map[offset]);
p->swap_map[offset] = SWAP_MAP_MAX;
}
result = 1;
#ifdef DEBUG_SWAP
printk("DebugVM: swap_duplicate(entry %08lx, count now %d)\n",
entry, p->swap_map[offset]);
#endif
out:
return;
return result;
bad_file:
printk("swap_duplicate: Trying to duplicate nonexistent swap-page\n");
printk(KERN_ERR
"swap_duplicate: entry %08lx, nonexistent swap file\n", entry);
goto out;
bad_offset:
printk("swap_duplicate: offset exceeds max\n");
printk(KERN_ERR
"swap_duplicate: entry %08lx, offset exceeds max\n", entry);
goto out;
bad_unused:
printk("swap_duplicate at %8p: unused page\n",
__builtin_return_address(0));
printk(KERN_ERR
"swap_duplicate at %8p: entry %08lx, unused page\n",
__builtin_return_address(0), entry);
goto out;
}
void remove_from_swap_cache(struct page *page)
static inline void remove_from_swap_cache(struct page *page)
{
if (!page->inode) {
printk ("VM: Removing swap cache page with zero inode hash "
......@@ -163,7 +174,7 @@ void remove_from_swap_cache(struct page *page)
}
int delete_from_swap_cache(struct page *page)
void delete_from_swap_cache(struct page *page)
{
#ifdef SWAP_CACHE_INFO
swap_cache_del_total++;
......@@ -180,9 +191,7 @@ int delete_from_swap_cache(struct page *page)
#endif
remove_from_swap_cache (page);
swap_free (entry);
return 1;
}
return 0;
}
/*
......@@ -218,57 +227,67 @@ static struct page * lookup_swap_cache(unsigned long entry)
found = find_page(&swapper_inode, entry);
if (!found)
return 0;
if (found->inode != &swapper_inode
|| !PageSwapCache(found)) {
__free_page(found);
printk ("VM: Found a non-swapper swap page!\n");
return 0;
}
if (found->inode != &swapper_inode || !PageSwapCache(found))
goto out_bad;
if (!PageLocked(found))
return found;
__free_page(found);
__wait_on_page(found);
}
out_bad:
printk (KERN_ERR "VM: Found a non-swapper swap page!\n");
__free_page(found);
return 0;
}
/*
* Locate a page of swap in physical memory, reserving swap cache space
* and reading the disk if it is not already cached. If wait==0, we are
* only doing readahead, so don't worry if the page is already locked.
*
* A failure return means that either the page allocation failed or that
* the swap entry is no longer in use.
*/
struct page * read_swap_cache_async(unsigned long entry, int wait)
{
struct page *found_page, *new_page = 0;
unsigned long new_page_addr = 0;
struct page *found_page, *new_page;
unsigned long new_page_addr;
#ifdef DEBUG_SWAP
printk("DebugVM: read_swap_cache_async entry %08lx%s\n",
entry, wait ? ", wait" : "");
#endif
repeat:
/*
* Look for the page in the swap cache.
*/
found_page = lookup_swap_cache(entry);
if (found_page) {
if (new_page)
__free_page(new_page);
return found_page;
}
if (found_page)
goto out;
new_page_addr = __get_free_page(GFP_KERNEL);
if (!new_page_addr)
goto out; /* Out of memory */
new_page = mem_map + MAP_NR(new_page_addr);
/*
* Check the swap cache again, in case we stalled above.
*/
found_page = lookup_swap_cache(entry);
if (found_page)
goto out_free_page;
/*
* Make sure the swap entry is still in use.
*/
if (!swap_duplicate(entry)) /* Account for the swap cache */
goto out_free_page;
/*
* Add it to the swap cache and read its contents.
*/
if (!add_to_swap_cache(new_page, entry))
goto out_free_page;
/* The entry is not present. Lock down a new page, add it to
* the swap cache and read its contents. */
if (!new_page) {
new_page_addr = __get_free_page(GFP_KERNEL);
if (!new_page_addr)
return 0; /* Out of memory */
new_page = mem_map + MAP_NR(new_page_addr);
goto repeat; /* We might have stalled */
}
if (!add_to_swap_cache(new_page, entry)) {
free_page(new_page_addr);
return 0;
}
swap_duplicate(entry); /* Account for the swap cache */
set_bit(PG_locked, &new_page->flags);
rw_swap_page(READ, entry, (char *) new_page_addr, wait);
#ifdef DEBUG_SWAP
......@@ -277,5 +296,9 @@ struct page * read_swap_cache_async(unsigned long entry, int wait)
entry, (char *) page_address(new_page));
#endif
return new_page;
}
out_free_page:
__free_page(new_page);
out:
return found_page;
}
......@@ -297,30 +297,36 @@ static int try_to_unuse(unsigned int type)
{
struct swap_info_struct * si = &swap_info[type];
struct task_struct *p;
unsigned long page = 0;
struct page *page_map;
unsigned long entry;
unsigned long entry, page;
int i;
while (1) {
/*
* Find a swap page in use and read it in.
*/
for (i = 1 , entry = 0; i < si->max ; i++) {
for (i = 1; i < si->max ; i++) {
if (si->swap_map[i] > 0 && si->swap_map[i] != SWAP_MAP_BAD) {
entry = SWP_ENTRY(type, i);
break;
goto found_entry;
}
}
if (!entry)
break;
break;
found_entry:
entry = SWP_ENTRY(type, i);
/* Get a page for the entry, using the existing swap
cache page if there is one. Otherwise, get a clean
page and read the swap into it. */
page_map = read_swap_cache(entry);
if (!page_map)
return -ENOMEM;
if (!page_map) {
/*
* Continue searching if the entry became unused.
*/
if (si->swap_map[i] == 0)
continue;
return -ENOMEM;
}
page = page_address(page_map);
read_lock(&tasklist_lock);
for_each_task(p)
......@@ -331,11 +337,15 @@ static int try_to_unuse(unsigned int type)
page we've been using. */
if (PageSwapCache(page_map))
delete_from_swap_cache(page_map);
free_page(page);
__free_page(page_map);
/*
* Check for and clear any overflowed swap map counts.
*/
if (si->swap_map[i] != 0) {
if (si->swap_map[i] != SWAP_MAP_MAX)
printk("try_to_unuse: entry %08lx "
"not in use\n", entry);
printk(KERN_ERR
"try_to_unuse: entry %08lx count=%d\n",
entry, si->swap_map[i]);
si->swap_map[i] = 0;
nr_swap_pages++;
}
......@@ -376,10 +386,9 @@ asmlinkage int sys_swapoff(const char * specialfile)
prev = type;
}
err = -EINVAL;
if (type < 0){
dput(dentry);
goto out;
}
if (type < 0)
goto out_dput;
if (prev < 0) {
swap_list.head = p->next;
} else {
......@@ -392,7 +401,6 @@ asmlinkage int sys_swapoff(const char * specialfile)
p->flags = SWP_USED;
err = try_to_unuse(type);
if (err) {
dput(dentry);
/* re-insert swap space back into swap_list */
for (prev = -1, i = swap_list.head; i >= 0; prev = i, i = swap_info[i].next)
if (p->prio >= swap_info[i].prio)
......@@ -403,7 +411,7 @@ asmlinkage int sys_swapoff(const char * specialfile)
else
swap_info[prev].next = p - swap_info;
p->flags = SWP_WRITEOK;
goto out;
goto out_dput;
}
if(p->swap_device){
memset(&filp, 0, sizeof(filp));
......@@ -418,9 +426,9 @@ asmlinkage int sys_swapoff(const char * specialfile)
}
dput(dentry);
nr_swap_pages -= p->pages;
dput(p->swap_file);
dentry = p->swap_file;
p->swap_file = NULL;
nr_swap_pages -= p->pages;
p->swap_device = 0;
vfree(p->swap_map);
p->swap_map = NULL;
......@@ -428,6 +436,9 @@ asmlinkage int sys_swapoff(const char * specialfile)
p->swap_lockmap = NULL;
p->flags = 0;
err = 0;
out_dput:
dput(dentry);
out:
unlock_kernel();
return err;
......@@ -719,4 +730,3 @@ void si_swapinfo(struct sysinfo *val)
val->totalswap <<= PAGE_SHIFT;
return;
}
......@@ -23,7 +23,6 @@
* NOTE: It is just working model of real NAT.
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/sched.h>
......
......@@ -573,11 +573,25 @@ unsigned int tcp_poll(struct file * file, struct socket *sock, poll_table *wait)
mask = 0;
if (sk->err)
mask = POLLERR;
/*
* POLLHUP is certainly not done right. But poll() doesn't
* have a notion of HUP in just one direction, and for a
* socket the read side is more interesting.
*
* Some poll() documentation says that POLLHUP is incompatible
* with the POLLOUT/POLLWR flags, so somebody should check this
* all. But careful, it tends to be safer to return too many
* bits than too few, and you can easily break real applications
* if you don't tell them that something has hung up!
*
* Check-me.
*/
if (sk->shutdown & RCV_SHUTDOWN)
mask |= POLLHUP;
/* Connected? */
if ((1 << sk->state) & ~(TCPF_SYN_SENT|TCPF_SYN_RECV|TCPF_CLOSE)) {
if (sk->shutdown & RCV_SHUTDOWN)
mask |= POLLHUP;
if ((1 << sk->state) & ~(TCPF_SYN_SENT|TCPF_SYN_RECV)) {
if ((tp->rcv_nxt != tp->copied_seq) &&
(tp->urg_seq != tp->copied_seq ||
tp->rcv_nxt != tp->copied_seq+1 ||
......
......@@ -5,7 +5,7 @@
*
* The User Datagram Protocol (UDP).
*
* Version: $Id: udp.c,v 1.59 1998/08/27 16:54:55 davem Exp $
* Version: $Id: udp.c,v 1.61 1998/08/29 17:11:10 freitag Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
......@@ -82,7 +82,7 @@
MUST provide facility for checksumming (OK)
MAY allow application to control checksumming (OK)
MUST default to checksumming on (OK)
MUST discard silently datagrams with bad csums (OK)
MUST discard silently datagrams with bad csums (OK, except during debugging)
4.1.3.5 (UDP Multihoming)
MUST allow application to specify source address (OK)
SHOULD be able to communicate the chosen src addr up to application
......@@ -95,14 +95,12 @@
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/fcntl.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/in.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/termios.h>
#include <linux/mm.h>
#include <linux/config.h>
#include <linux/inet.h>
......@@ -110,14 +108,12 @@
#include <net/snmp.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/tcp.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/udp.h>
#include <net/icmp.h>
#include <net/route.h>
#include <net/checksum.h>
#include <linux/ipsec.h>
/*
* Snmp MIB for the UDP layer
......@@ -679,21 +675,6 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len)
ufh.uh.dest = usin->sin_port;
if (ufh.uh.dest == 0)
return -EINVAL;
/* XXX: is a one-behind cache for the dst_entry worth it?
Nope. ip_route_output is slower than nothing, but it
is enough fast to forget about caching its results.
Really, checking route validity in general case
is not much faster complete lookup.
It was main reason why I removed it from 2.1.
The second reason was that idle sockets held
a lot of stray destinations. --ANK
Look: route depends on ALL the options,
checking its validity is exactly on cycle
of ip_route_output(). We save only start_bh_atomic()
in SMP case. On UP we save nothing. --ANK
*/
} else {
if (sk->state != TCP_ESTABLISHED)
return -ENOTCONN;
......@@ -791,8 +772,7 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len)
/* RFC1122: OK. Provides the checksumming facility (MUST) as per */
/* 4.1.3.4. It's configurable by the application via setsockopt() */
/* (MAY) and it defaults to on (MUST). Almost makes up for the */
/* violation above. -- MS */
/* (MAY) and it defaults to on (MUST). */
err = ip_build_xmit(sk,sk->no_check ? udp_getfrag_nosum : udp_getfrag,
&ufh, ulen, &ipc, rt, msg->msg_flags);
......@@ -854,44 +834,10 @@ int udp_ioctl(struct sock *sk, int cmd, unsigned long arg)
return(0);
}
#ifdef CONFIG_FILTER
#if defined(CONFIG_FILTER) || !defined(HAVE_CSUM_COPY_USER)
#undef CONFIG_UDP_DELAY_CSUM
#endif
#ifdef CONFIG_UDP_DELAY_CSUM
/* Please, read comments in net/checksum.h, asm/checksum.h
I commented out csum_partial_copy_to_user there because it did not
verify_area. Now I am even wondered, how clever was I that time 8)8)
If I did not it, I would step into this hole again. --ANK
*/
#ifndef _HAVE_ARCH_COPY_AND_CSUM_TO_USER
#ifdef __i386__
static __inline__
unsigned int csum_and_copy_to_user (const char *src, char *dst,
int len, int sum, int *err_ptr)
{
int *src_err_ptr=NULL;
if (verify_area(VERIFY_WRITE, dst, len) == 0)
return csum_partial_copy_generic(src, dst, len, sum, src_err_ptr, err_ptr);
if (len)
*err_ptr = -EFAULT;
return sum;
}
#elif defined(__sparc__)
#define csum_and_copy_to_user csum_partial_copy_to_user
#else
#undef CONFIG_UDP_DELAY_CSUM
#endif
#endif
#endif
/*
* This should be easy, if there is something there we
* return it, otherwise we block.
......@@ -943,32 +889,21 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
copied);
} else if (copied > msg->msg_iov[0].iov_len || (msg->msg_flags&MSG_TRUNC)) {
if (csum_fold(csum_partial(skb->h.raw, ntohs(skb->h.uh->len), skb->csum))) {
udp_statistics.UdpInErrors++;
/* Error for blocking case is chosen to masquerade
as some normal condition.
*/
err = (msg->msg_flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;
goto out_free;
}
if (csum_fold(csum_partial(skb->h.raw, ntohs(skb->h.uh->len), skb->csum)))
goto csum_copy_err;
err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
copied);
} else {
unsigned int csum = csum_partial(skb->h.raw, sizeof(struct udphdr), skb->csum);
unsigned int csum;
err = 0;
csum = csum_and_copy_to_user((char*)&skb->h.uh[1], msg->msg_iov[0].iov_base, copied, csum, &err);
csum = csum_partial(skb->h.raw, sizeof(struct udphdr), skb->csum);
csum = csum_and_copy_to_user((char*)&skb->h.uh[1], msg->msg_iov[0].iov_base,
copied, csum, &err);
if (err)
goto out_free;
if (csum_fold(csum)) {
udp_statistics.UdpInErrors++;
/* Error for blocking case is chosen to masquerade
as some normal condition.
*/
err = (msg->msg_flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;
goto out_free;
}
if (csum_fold(csum))
goto csum_copy_err;
}
#endif
if (err)
......@@ -1007,6 +942,18 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
skb_free_datagram(sk, skb);
out:
return err;
#ifdef CONFIG_UDP_DELAY_CSUM
csum_copy_err:
udp_statistics.UdpInErrors++;
skb_free_datagram(sk, skb);
/*
* Error for blocking case is chosen to masquerade
* as some normal condition.
*/
return (msg->msg_flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;
#endif
}
int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
......@@ -1074,16 +1021,6 @@ static void udp_close(struct sock *sk, unsigned long timeout)
static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
{
/*
* Check the security clearance
*/
if(!ipsec_sk_policy(sk,skb))
{
kfree_skb(skb);
return(0);
}
/*
* Charge it to the socket, dropping if the queue is full.
*/
......@@ -1209,40 +1146,14 @@ int udp_rcv(struct sk_buff *skb, unsigned short len)
if (uh->check &&
(((skb->ip_summed==CHECKSUM_HW)&&udp_check(uh,ulen,saddr,daddr,skb->csum)) ||
((skb->ip_summed==CHECKSUM_NONE) &&
(udp_check(uh,ulen,saddr,daddr, csum_partial((char*)uh, ulen, 0)))))) {
/* <mea@utu.fi> wants to know, who sent it, to
go and stomp on the garbage sender... */
/* RFC1122: OK. Discards the bad packet silently (as far as */
/* the network is concerned, anyway) as per 4.1.3.4 (MUST). */
NETDEBUG(printk(KERN_DEBUG "UDP: bad checksum. From %08lX:%d to %08lX:%d ulen %d\n",
ntohl(saddr),ntohs(uh->source),
ntohl(daddr),ntohs(uh->dest),
ulen));
udp_statistics.UdpInErrors++;
kfree_skb(skb);
return(0);
}
(udp_check(uh,ulen,saddr,daddr, csum_partial((char*)uh, ulen, 0))))))
goto csum_error;
#else
if (uh->check==0)
skb->ip_summed = CHECKSUM_UNNECESSARY;
else if (skb->ip_summed==CHECKSUM_HW) {
if (udp_check(uh,ulen,saddr,daddr,skb->csum)) {
/* <mea@utu.fi> wants to know, who sent it, to
go and stomp on the garbage sender... */
/* RFC1122: OK. Discards the bad packet silently (as far as */
/* the network is concerned, anyway) as per 4.1.3.4 (MUST). */
NETDEBUG(printk(KERN_DEBUG "UDP: bad checksum. From %08lX:%d to %08lX:%d ulen %d\n",
ntohl(saddr),ntohs(uh->source),
ntohl(daddr),ntohs(uh->dest),
ulen));
udp_statistics.UdpInErrors++;
kfree_skb(skb);
return(0);
}
if (udp_check(uh,ulen,saddr,daddr,skb->csum))
goto csum_error;
skb->ip_summed = CHECKSUM_UNNECESSARY;
} else if (skb->ip_summed != CHECKSUM_UNNECESSARY)
skb->csum = csum_tcpudp_nofold(saddr, daddr, ulen, IPPROTO_UDP, 0);
......@@ -1263,21 +1174,8 @@ int udp_rcv(struct sk_buff *skb, unsigned short len)
if (sk == NULL) {
#ifdef CONFIG_UDP_DELAY_CSUM
if (skb->ip_summed != CHECKSUM_UNNECESSARY &&
csum_fold(csum_partial((char*)uh, ulen, skb->csum))) {
/* <mea@utu.fi> wants to know, who sent it, to
go and stomp on the garbage sender... */
/* RFC1122: OK. Discards the bad packet silently (as far as */
/* the network is concerned, anyway) as per 4.1.3.4 (MUST). */
NETDEBUG(printk(KERN_DEBUG "UDP: bad checksum. From %08lX:%d to %08lX:%d ulen %d\n",
ntohl(saddr),ntohs(uh->source),
ntohl(daddr),ntohs(uh->dest),
ulen));
udp_statistics.UdpInErrors++;
kfree_skb(skb);
return(0);
}
csum_fold(csum_partial((char*)uh, ulen, skb->csum)))
goto csum_error;
#endif
udp_statistics.UdpNoPorts++;
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
......@@ -1291,6 +1189,19 @@ int udp_rcv(struct sk_buff *skb, unsigned short len)
}
udp_deliver(sk, skb);
return 0;
csum_error:
/*
* RFC1122: OK. Discards the bad packet silently (as far as
* the network is concerned, anyway) as per 4.1.3.4 (MUST).
*/
NETDEBUG(printk(KERN_DEBUG "UDP: bad checksum. From %08lX:%d to %08lX:%d ulen %d\n",
ntohl(saddr),ntohs(uh->source),
ntohl(daddr),ntohs(uh->dest),
ulen));
udp_statistics.UdpInErrors++;
kfree_skb(skb);
return(0);
}
struct proto udp_prot = {
......@@ -1320,7 +1231,7 @@ struct proto udp_prot = {
udp_v4_verify_bind, /* verify_bind */
128, /* max_header */
0, /* retransmits */
"UDP", /* name */
"UDP", /* name */
0, /* inuse */
0 /* highestinuse */
};
......@@ -15,6 +15,7 @@
* 2 of the License, or (at your option) any later version.
*/
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
......
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