Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Цей коміт міститься в:
87
fs/ncpfs/Kconfig
Звичайний файл
87
fs/ncpfs/Kconfig
Звичайний файл
@@ -0,0 +1,87 @@
|
||||
#
|
||||
# NCP Filesystem configuration
|
||||
#
|
||||
config NCPFS_PACKET_SIGNING
|
||||
bool "Packet signatures"
|
||||
depends on NCP_FS
|
||||
help
|
||||
NCP allows packets to be signed for stronger security. If you want
|
||||
security, say Y. Normal users can leave it off. To be able to use
|
||||
packet signing you must use ncpfs > 2.0.12.
|
||||
|
||||
config NCPFS_IOCTL_LOCKING
|
||||
bool "Proprietary file locking"
|
||||
depends on NCP_FS
|
||||
help
|
||||
Allows locking of records on remote volumes. Say N unless you have
|
||||
special applications which are able to utilize this locking scheme.
|
||||
|
||||
config NCPFS_STRONG
|
||||
bool "Clear remove/delete inhibit when needed"
|
||||
depends on NCP_FS
|
||||
help
|
||||
Allows manipulation of files flagged as Delete or Rename Inhibit.
|
||||
To use this feature you must mount volumes with the ncpmount
|
||||
parameter "-s" (ncpfs-2.0.12 and newer). Say Y unless you are not
|
||||
mounting volumes with -f 444.
|
||||
|
||||
config NCPFS_NFS_NS
|
||||
bool "Use NFS namespace if available"
|
||||
depends on NCP_FS
|
||||
help
|
||||
Allows you to utilize NFS namespace on NetWare servers. It brings
|
||||
you case sensitive filenames. Say Y. You can disable it at
|
||||
mount-time with the `-N nfs' parameter of ncpmount.
|
||||
|
||||
config NCPFS_OS2_NS
|
||||
bool "Use LONG (OS/2) namespace if available"
|
||||
depends on NCP_FS
|
||||
help
|
||||
Allows you to utilize OS2/LONG namespace on NetWare servers.
|
||||
Filenames in this namespace are limited to 255 characters, they are
|
||||
case insensitive, and case in names is preserved. Say Y. You can
|
||||
disable it at mount time with the -N os2 parameter of ncpmount.
|
||||
|
||||
config NCPFS_SMALLDOS
|
||||
bool "Lowercase DOS filenames"
|
||||
depends on NCP_FS
|
||||
---help---
|
||||
If you say Y here, every filename on a NetWare server volume using
|
||||
the OS2/LONG namespace and created under DOS or on a volume using
|
||||
DOS namespace will be converted to lowercase characters.
|
||||
Saying N here will give you these filenames in uppercase.
|
||||
|
||||
This is only a cosmetic option since the OS2/LONG namespace is case
|
||||
insensitive. The only major reason for this option is backward
|
||||
compatibility when moving from DOS to OS2/LONG namespace support.
|
||||
Long filenames (created by Win95) will not be affected.
|
||||
|
||||
This option does not solve the problem that filenames appear
|
||||
differently under Linux and under Windows, since Windows does an
|
||||
additional conversions on the client side. You can achieve similar
|
||||
effects by saying Y to "Allow using of Native Language Support"
|
||||
below.
|
||||
|
||||
config NCPFS_NLS
|
||||
bool "Use Native Language Support"
|
||||
depends on NCP_FS
|
||||
select NLS
|
||||
help
|
||||
Allows you to use codepages and I/O charsets for file name
|
||||
translation between the server file system and input/output. This
|
||||
may be useful, if you want to access the server with other operating
|
||||
systems, e.g. Windows 95. See also NLS for more Information.
|
||||
|
||||
To select codepages and I/O charsets use ncpfs-2.2.0.13 or newer.
|
||||
|
||||
config NCPFS_EXTRAS
|
||||
bool "Enable symbolic links and execute flags"
|
||||
depends on NCP_FS
|
||||
help
|
||||
This enables the use of symbolic links and an execute permission
|
||||
bit on NCPFS. The file server need not have long name space or NFS
|
||||
name space loaded for these to work.
|
||||
|
||||
To use the new attributes, it is recommended to use the flags
|
||||
'-f 600 -d 755' on the ncpmount command line.
|
||||
|
16
fs/ncpfs/Makefile
Звичайний файл
16
fs/ncpfs/Makefile
Звичайний файл
@@ -0,0 +1,16 @@
|
||||
#
|
||||
# Makefile for the linux ncp filesystem routines.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_NCP_FS) += ncpfs.o
|
||||
|
||||
ncpfs-y := dir.o file.o inode.o ioctl.o mmap.o ncplib_kernel.o sock.o \
|
||||
ncpsign_kernel.o getopt.o
|
||||
|
||||
ncpfs-$(CONFIG_NCPFS_EXTRAS) += symlink.o
|
||||
ncpfs-$(CONFIG_NCPFS_NFS_NS) += symlink.o
|
||||
|
||||
# If you want debugging output, please uncomment the following line
|
||||
# EXTRA_CFLAGS += -DDEBUG_NCP=1
|
||||
|
||||
CFLAGS_ncplib_kernel.o := -finline-functions
|
1260
fs/ncpfs/dir.c
Звичайний файл
1260
fs/ncpfs/dir.c
Звичайний файл
Різницю між файлами не показано, бо вона завелика
Завантажити різницю
300
fs/ncpfs/file.c
Звичайний файл
300
fs/ncpfs/file.c
Звичайний файл
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* file.c
|
||||
*
|
||||
* Copyright (C) 1995, 1996 by Volker Lendecke
|
||||
* Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
|
||||
*
|
||||
*/
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#include <linux/time.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/smp_lock.h>
|
||||
|
||||
#include <linux/ncp_fs.h>
|
||||
#include "ncplib_kernel.h"
|
||||
|
||||
static int ncp_fsync(struct file *file, struct dentry *dentry, int datasync)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open a file with the specified read/write mode.
|
||||
*/
|
||||
int ncp_make_open(struct inode *inode, int right)
|
||||
{
|
||||
int error;
|
||||
int access;
|
||||
|
||||
error = -EINVAL;
|
||||
if (!inode) {
|
||||
printk(KERN_ERR "ncp_make_open: got NULL inode\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
DPRINTK("ncp_make_open: opened=%d, volume # %u, dir entry # %u\n",
|
||||
atomic_read(&NCP_FINFO(inode)->opened),
|
||||
NCP_FINFO(inode)->volNumber,
|
||||
NCP_FINFO(inode)->dirEntNum);
|
||||
error = -EACCES;
|
||||
down(&NCP_FINFO(inode)->open_sem);
|
||||
if (!atomic_read(&NCP_FINFO(inode)->opened)) {
|
||||
struct ncp_entry_info finfo;
|
||||
int result;
|
||||
|
||||
/* tries max. rights */
|
||||
finfo.access = O_RDWR;
|
||||
result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
|
||||
inode, NULL, OC_MODE_OPEN,
|
||||
0, AR_READ | AR_WRITE, &finfo);
|
||||
if (!result)
|
||||
goto update;
|
||||
/* RDWR did not succeeded, try readonly or writeonly as requested */
|
||||
switch (right) {
|
||||
case O_RDONLY:
|
||||
finfo.access = O_RDONLY;
|
||||
result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
|
||||
inode, NULL, OC_MODE_OPEN,
|
||||
0, AR_READ, &finfo);
|
||||
break;
|
||||
case O_WRONLY:
|
||||
finfo.access = O_WRONLY;
|
||||
result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
|
||||
inode, NULL, OC_MODE_OPEN,
|
||||
0, AR_WRITE, &finfo);
|
||||
break;
|
||||
}
|
||||
if (result) {
|
||||
PPRINTK("ncp_make_open: failed, result=%d\n", result);
|
||||
goto out_unlock;
|
||||
}
|
||||
/*
|
||||
* Update the inode information.
|
||||
*/
|
||||
update:
|
||||
ncp_update_inode(inode, &finfo);
|
||||
atomic_set(&NCP_FINFO(inode)->opened, 1);
|
||||
}
|
||||
|
||||
access = NCP_FINFO(inode)->access;
|
||||
PPRINTK("ncp_make_open: file open, access=%x\n", access);
|
||||
if (access == right || access == O_RDWR) {
|
||||
atomic_inc(&NCP_FINFO(inode)->opened);
|
||||
error = 0;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
up(&NCP_FINFO(inode)->open_sem);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ncp_file_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct dentry *dentry = file->f_dentry;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
size_t already_read = 0;
|
||||
off_t pos;
|
||||
size_t bufsize;
|
||||
int error;
|
||||
void* freepage;
|
||||
size_t freelen;
|
||||
|
||||
DPRINTK("ncp_file_read: enter %s/%s\n",
|
||||
dentry->d_parent->d_name.name, dentry->d_name.name);
|
||||
|
||||
if (!ncp_conn_valid(NCP_SERVER(inode)))
|
||||
return -EIO;
|
||||
|
||||
pos = *ppos;
|
||||
|
||||
if ((ssize_t) count < 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!count)
|
||||
return 0;
|
||||
if (pos > inode->i_sb->s_maxbytes)
|
||||
return 0;
|
||||
if (pos + count > inode->i_sb->s_maxbytes) {
|
||||
count = inode->i_sb->s_maxbytes - pos;
|
||||
}
|
||||
|
||||
error = ncp_make_open(inode, O_RDONLY);
|
||||
if (error) {
|
||||
DPRINTK(KERN_ERR "ncp_file_read: open failed, error=%d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
bufsize = NCP_SERVER(inode)->buffer_size;
|
||||
|
||||
error = -EIO;
|
||||
freelen = ncp_read_bounce_size(bufsize);
|
||||
freepage = vmalloc(freelen);
|
||||
if (!freepage)
|
||||
goto outrel;
|
||||
error = 0;
|
||||
/* First read in as much as possible for each bufsize. */
|
||||
while (already_read < count) {
|
||||
int read_this_time;
|
||||
size_t to_read = min_t(unsigned int,
|
||||
bufsize - (pos % bufsize),
|
||||
count - already_read);
|
||||
|
||||
error = ncp_read_bounce(NCP_SERVER(inode),
|
||||
NCP_FINFO(inode)->file_handle,
|
||||
pos, to_read, buf, &read_this_time,
|
||||
freepage, freelen);
|
||||
if (error) {
|
||||
error = -EIO; /* NW errno -> Linux errno */
|
||||
break;
|
||||
}
|
||||
pos += read_this_time;
|
||||
buf += read_this_time;
|
||||
already_read += read_this_time;
|
||||
|
||||
if (read_this_time != to_read) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
vfree(freepage);
|
||||
|
||||
*ppos = pos;
|
||||
|
||||
file_accessed(file);
|
||||
|
||||
DPRINTK("ncp_file_read: exit %s/%s\n",
|
||||
dentry->d_parent->d_name.name, dentry->d_name.name);
|
||||
outrel:
|
||||
ncp_inode_close(inode);
|
||||
return already_read ? already_read : error;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ncp_file_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct dentry *dentry = file->f_dentry;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
size_t already_written = 0;
|
||||
off_t pos;
|
||||
size_t bufsize;
|
||||
int errno;
|
||||
void* bouncebuffer;
|
||||
|
||||
DPRINTK("ncp_file_write: enter %s/%s\n",
|
||||
dentry->d_parent->d_name.name, dentry->d_name.name);
|
||||
if (!ncp_conn_valid(NCP_SERVER(inode)))
|
||||
return -EIO;
|
||||
if ((ssize_t) count < 0)
|
||||
return -EINVAL;
|
||||
pos = *ppos;
|
||||
if (file->f_flags & O_APPEND) {
|
||||
pos = inode->i_size;
|
||||
}
|
||||
|
||||
if (pos + count > MAX_NON_LFS && !(file->f_flags&O_LARGEFILE)) {
|
||||
if (pos >= MAX_NON_LFS) {
|
||||
send_sig(SIGXFSZ, current, 0);
|
||||
return -EFBIG;
|
||||
}
|
||||
if (count > MAX_NON_LFS - (u32)pos) {
|
||||
count = MAX_NON_LFS - (u32)pos;
|
||||
}
|
||||
}
|
||||
if (pos >= inode->i_sb->s_maxbytes) {
|
||||
if (count || pos > inode->i_sb->s_maxbytes) {
|
||||
send_sig(SIGXFSZ, current, 0);
|
||||
return -EFBIG;
|
||||
}
|
||||
}
|
||||
if (pos + count > inode->i_sb->s_maxbytes) {
|
||||
count = inode->i_sb->s_maxbytes - pos;
|
||||
}
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
errno = ncp_make_open(inode, O_WRONLY);
|
||||
if (errno) {
|
||||
DPRINTK(KERN_ERR "ncp_file_write: open failed, error=%d\n", errno);
|
||||
return errno;
|
||||
}
|
||||
bufsize = NCP_SERVER(inode)->buffer_size;
|
||||
|
||||
already_written = 0;
|
||||
|
||||
bouncebuffer = vmalloc(bufsize);
|
||||
if (!bouncebuffer) {
|
||||
errno = -EIO; /* -ENOMEM */
|
||||
goto outrel;
|
||||
}
|
||||
while (already_written < count) {
|
||||
int written_this_time;
|
||||
size_t to_write = min_t(unsigned int,
|
||||
bufsize - (pos % bufsize),
|
||||
count - already_written);
|
||||
|
||||
if (copy_from_user(bouncebuffer, buf, to_write)) {
|
||||
errno = -EFAULT;
|
||||
break;
|
||||
}
|
||||
if (ncp_write_kernel(NCP_SERVER(inode),
|
||||
NCP_FINFO(inode)->file_handle,
|
||||
pos, to_write, bouncebuffer, &written_this_time) != 0) {
|
||||
errno = -EIO;
|
||||
break;
|
||||
}
|
||||
pos += written_this_time;
|
||||
buf += written_this_time;
|
||||
already_written += written_this_time;
|
||||
|
||||
if (written_this_time != to_write) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
vfree(bouncebuffer);
|
||||
|
||||
inode_update_time(inode, 1);
|
||||
|
||||
*ppos = pos;
|
||||
|
||||
if (pos > inode->i_size) {
|
||||
inode->i_size = pos;
|
||||
}
|
||||
DPRINTK("ncp_file_write: exit %s/%s\n",
|
||||
dentry->d_parent->d_name.name, dentry->d_name.name);
|
||||
outrel:
|
||||
ncp_inode_close(inode);
|
||||
return already_written ? already_written : errno;
|
||||
}
|
||||
|
||||
static int ncp_release(struct inode *inode, struct file *file) {
|
||||
if (ncp_make_closed(inode)) {
|
||||
DPRINTK("ncp_release: failed to close\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct file_operations ncp_file_operations =
|
||||
{
|
||||
.llseek = remote_llseek,
|
||||
.read = ncp_file_read,
|
||||
.write = ncp_file_write,
|
||||
.ioctl = ncp_ioctl,
|
||||
.mmap = ncp_mmap,
|
||||
.release = ncp_release,
|
||||
.fsync = ncp_fsync,
|
||||
};
|
||||
|
||||
struct inode_operations ncp_file_inode_operations =
|
||||
{
|
||||
.setattr = ncp_notify_change,
|
||||
};
|
75
fs/ncpfs/getopt.c
Звичайний файл
75
fs/ncpfs/getopt.c
Звичайний файл
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* getopt.c
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <asm/errno.h>
|
||||
|
||||
#include "getopt.h"
|
||||
|
||||
/**
|
||||
* ncp_getopt - option parser
|
||||
* @caller: name of the caller, for error messages
|
||||
* @options: the options string
|
||||
* @opts: an array of &struct option entries controlling parser operations
|
||||
* @optopt: output; will contain the current option
|
||||
* @optarg: output; will contain the value (if one exists)
|
||||
* @flag: output; may be NULL; should point to a long for or'ing flags
|
||||
* @value: output; may be NULL; will be overwritten with the integer value
|
||||
* of the current argument.
|
||||
*
|
||||
* Helper to parse options on the format used by mount ("a=b,c=d,e,f").
|
||||
* Returns opts->val if a matching entry in the 'opts' array is found,
|
||||
* 0 when no more tokens are found, -1 if an error is encountered.
|
||||
*/
|
||||
int ncp_getopt(const char *caller, char **options, const struct ncp_option *opts,
|
||||
char **optopt, char **optarg, unsigned long *value)
|
||||
{
|
||||
char *token;
|
||||
char *val;
|
||||
|
||||
do {
|
||||
if ((token = strsep(options, ",")) == NULL)
|
||||
return 0;
|
||||
} while (*token == '\0');
|
||||
if (optopt)
|
||||
*optopt = token;
|
||||
|
||||
if ((val = strchr (token, '=')) != NULL) {
|
||||
*val++ = 0;
|
||||
}
|
||||
*optarg = val;
|
||||
for (; opts->name; opts++) {
|
||||
if (!strcmp(opts->name, token)) {
|
||||
if (!val) {
|
||||
if (opts->has_arg & OPT_NOPARAM) {
|
||||
return opts->val;
|
||||
}
|
||||
printk(KERN_INFO "%s: the %s option requires an argument\n",
|
||||
caller, token);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (opts->has_arg & OPT_INT) {
|
||||
char* v;
|
||||
|
||||
*value = simple_strtoul(val, &v, 0);
|
||||
if (!*v) {
|
||||
return opts->val;
|
||||
}
|
||||
printk(KERN_INFO "%s: invalid numeric value in %s=%s\n",
|
||||
caller, token, val);
|
||||
return -EDOM;
|
||||
}
|
||||
if (opts->has_arg & OPT_STRING) {
|
||||
return opts->val;
|
||||
}
|
||||
printk(KERN_INFO "%s: unexpected argument %s to the %s option\n",
|
||||
caller, val, token);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
printk(KERN_INFO "%s: Unrecognized mount option %s\n", caller, token);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
16
fs/ncpfs/getopt.h
Звичайний файл
16
fs/ncpfs/getopt.h
Звичайний файл
@@ -0,0 +1,16 @@
|
||||
#ifndef _LINUX_GETOPT_H
|
||||
#define _LINUX_GETOPT_H
|
||||
|
||||
#define OPT_NOPARAM 1
|
||||
#define OPT_INT 2
|
||||
#define OPT_STRING 4
|
||||
struct ncp_option {
|
||||
const char *name;
|
||||
unsigned int has_arg;
|
||||
int val;
|
||||
};
|
||||
|
||||
extern int ncp_getopt(const char *caller, char **options, const struct ncp_option *opts,
|
||||
char **optopt, char **optarg, unsigned long *value);
|
||||
|
||||
#endif /* _LINUX_GETOPT_H */
|
1012
fs/ncpfs/inode.c
Звичайний файл
1012
fs/ncpfs/inode.c
Звичайний файл
Різницю між файлами не показано, бо вона завелика
Завантажити різницю
649
fs/ncpfs/ioctl.c
Звичайний файл
649
fs/ncpfs/ioctl.c
Звичайний файл
@@ -0,0 +1,649 @@
|
||||
/*
|
||||
* ioctl.c
|
||||
*
|
||||
* Copyright (C) 1995, 1996 by Volker Lendecke
|
||||
* Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
|
||||
* Modified 1998, 1999 Wolfram Pienkoss for NLS
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/highuid.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include <linux/ncp_fs.h>
|
||||
|
||||
#include "ncplib_kernel.h"
|
||||
|
||||
/* maximum limit for ncp_objectname_ioctl */
|
||||
#define NCP_OBJECT_NAME_MAX_LEN 4096
|
||||
/* maximum limit for ncp_privatedata_ioctl */
|
||||
#define NCP_PRIVATE_DATA_MAX_LEN 8192
|
||||
/* maximum negotiable packet size */
|
||||
#define NCP_PACKET_SIZE_INTERNAL 65536
|
||||
|
||||
static int
|
||||
ncp_get_fs_info(struct ncp_server* server, struct inode* inode, struct ncp_fs_info __user *arg)
|
||||
{
|
||||
struct ncp_fs_info info;
|
||||
|
||||
if ((permission(inode, MAY_WRITE, NULL) != 0)
|
||||
&& (current->uid != server->m.mounted_uid)) {
|
||||
return -EACCES;
|
||||
}
|
||||
if (copy_from_user(&info, arg, sizeof(info)))
|
||||
return -EFAULT;
|
||||
|
||||
if (info.version != NCP_GET_FS_INFO_VERSION) {
|
||||
DPRINTK("info.version invalid: %d\n", info.version);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* TODO: info.addr = server->m.serv_addr; */
|
||||
SET_UID(info.mounted_uid, server->m.mounted_uid);
|
||||
info.connection = server->connection;
|
||||
info.buffer_size = server->buffer_size;
|
||||
info.volume_number = NCP_FINFO(inode)->volNumber;
|
||||
info.directory_id = NCP_FINFO(inode)->DosDirNum;
|
||||
|
||||
if (copy_to_user(arg, &info, sizeof(info)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ncp_get_fs_info_v2(struct ncp_server* server, struct inode* inode, struct ncp_fs_info_v2 __user * arg)
|
||||
{
|
||||
struct ncp_fs_info_v2 info2;
|
||||
|
||||
if ((permission(inode, MAY_WRITE, NULL) != 0)
|
||||
&& (current->uid != server->m.mounted_uid)) {
|
||||
return -EACCES;
|
||||
}
|
||||
if (copy_from_user(&info2, arg, sizeof(info2)))
|
||||
return -EFAULT;
|
||||
|
||||
if (info2.version != NCP_GET_FS_INFO_VERSION_V2) {
|
||||
DPRINTK("info.version invalid: %d\n", info2.version);
|
||||
return -EINVAL;
|
||||
}
|
||||
info2.mounted_uid = server->m.mounted_uid;
|
||||
info2.connection = server->connection;
|
||||
info2.buffer_size = server->buffer_size;
|
||||
info2.volume_number = NCP_FINFO(inode)->volNumber;
|
||||
info2.directory_id = NCP_FINFO(inode)->DosDirNum;
|
||||
info2.dummy1 = info2.dummy2 = info2.dummy3 = 0;
|
||||
|
||||
if (copy_to_user(arg, &info2, sizeof(info2)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NCPFS_NLS
|
||||
/* Here we are select the iocharset and the codepage for NLS.
|
||||
* Thanks Petr Vandrovec for idea and many hints.
|
||||
*/
|
||||
static int
|
||||
ncp_set_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg)
|
||||
{
|
||||
struct ncp_nls_ioctl user;
|
||||
struct nls_table *codepage;
|
||||
struct nls_table *iocharset;
|
||||
struct nls_table *oldset_io;
|
||||
struct nls_table *oldset_cp;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
if (server->root_setuped)
|
||||
return -EBUSY;
|
||||
|
||||
if (copy_from_user(&user, arg, sizeof(user)))
|
||||
return -EFAULT;
|
||||
|
||||
codepage = NULL;
|
||||
user.codepage[NCP_IOCSNAME_LEN] = 0;
|
||||
if (!user.codepage[0] || !strcmp(user.codepage, "default"))
|
||||
codepage = load_nls_default();
|
||||
else {
|
||||
codepage = load_nls(user.codepage);
|
||||
if (!codepage) {
|
||||
return -EBADRQC;
|
||||
}
|
||||
}
|
||||
|
||||
iocharset = NULL;
|
||||
user.iocharset[NCP_IOCSNAME_LEN] = 0;
|
||||
if (!user.iocharset[0] || !strcmp(user.iocharset, "default")) {
|
||||
iocharset = load_nls_default();
|
||||
NCP_CLR_FLAG(server, NCP_FLAG_UTF8);
|
||||
} else if (!strcmp(user.iocharset, "utf8")) {
|
||||
iocharset = load_nls_default();
|
||||
NCP_SET_FLAG(server, NCP_FLAG_UTF8);
|
||||
} else {
|
||||
iocharset = load_nls(user.iocharset);
|
||||
if (!iocharset) {
|
||||
unload_nls(codepage);
|
||||
return -EBADRQC;
|
||||
}
|
||||
NCP_CLR_FLAG(server, NCP_FLAG_UTF8);
|
||||
}
|
||||
|
||||
oldset_cp = server->nls_vol;
|
||||
server->nls_vol = codepage;
|
||||
oldset_io = server->nls_io;
|
||||
server->nls_io = iocharset;
|
||||
|
||||
if (oldset_cp)
|
||||
unload_nls(oldset_cp);
|
||||
if (oldset_io)
|
||||
unload_nls(oldset_io);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ncp_get_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg)
|
||||
{
|
||||
struct ncp_nls_ioctl user;
|
||||
int len;
|
||||
|
||||
memset(&user, 0, sizeof(user));
|
||||
if (server->nls_vol && server->nls_vol->charset) {
|
||||
len = strlen(server->nls_vol->charset);
|
||||
if (len > NCP_IOCSNAME_LEN)
|
||||
len = NCP_IOCSNAME_LEN;
|
||||
strncpy(user.codepage, server->nls_vol->charset, len);
|
||||
user.codepage[len] = 0;
|
||||
}
|
||||
|
||||
if (NCP_IS_FLAG(server, NCP_FLAG_UTF8))
|
||||
strcpy(user.iocharset, "utf8");
|
||||
else if (server->nls_io && server->nls_io->charset) {
|
||||
len = strlen(server->nls_io->charset);
|
||||
if (len > NCP_IOCSNAME_LEN)
|
||||
len = NCP_IOCSNAME_LEN;
|
||||
strncpy(user.iocharset, server->nls_io->charset, len);
|
||||
user.iocharset[len] = 0;
|
||||
}
|
||||
|
||||
if (copy_to_user(arg, &user, sizeof(user)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_NCPFS_NLS */
|
||||
|
||||
int ncp_ioctl(struct inode *inode, struct file *filp,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct ncp_server *server = NCP_SERVER(inode);
|
||||
int result;
|
||||
struct ncp_ioctl_request request;
|
||||
char* bouncebuffer;
|
||||
void __user *argp = (void __user *)arg;
|
||||
|
||||
switch (cmd) {
|
||||
case NCP_IOC_NCPREQUEST:
|
||||
|
||||
if ((permission(inode, MAY_WRITE, NULL) != 0)
|
||||
&& (current->uid != server->m.mounted_uid)) {
|
||||
return -EACCES;
|
||||
}
|
||||
if (copy_from_user(&request, argp, sizeof(request)))
|
||||
return -EFAULT;
|
||||
|
||||
if ((request.function > 255)
|
||||
|| (request.size >
|
||||
NCP_PACKET_SIZE - sizeof(struct ncp_request_header))) {
|
||||
return -EINVAL;
|
||||
}
|
||||
bouncebuffer = vmalloc(NCP_PACKET_SIZE_INTERNAL);
|
||||
if (!bouncebuffer)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(bouncebuffer, request.data, request.size)) {
|
||||
vfree(bouncebuffer);
|
||||
return -EFAULT;
|
||||
}
|
||||
ncp_lock_server(server);
|
||||
|
||||
/* FIXME: We hack around in the server's structures
|
||||
here to be able to use ncp_request */
|
||||
|
||||
server->has_subfunction = 0;
|
||||
server->current_size = request.size;
|
||||
memcpy(server->packet, bouncebuffer, request.size);
|
||||
|
||||
result = ncp_request2(server, request.function,
|
||||
bouncebuffer, NCP_PACKET_SIZE_INTERNAL);
|
||||
if (result < 0)
|
||||
result = -EIO;
|
||||
else
|
||||
result = server->reply_size;
|
||||
ncp_unlock_server(server);
|
||||
DPRINTK("ncp_ioctl: copy %d bytes\n",
|
||||
result);
|
||||
if (result >= 0)
|
||||
if (copy_to_user(request.data, bouncebuffer, result))
|
||||
result = -EFAULT;
|
||||
vfree(bouncebuffer);
|
||||
return result;
|
||||
|
||||
case NCP_IOC_CONN_LOGGED_IN:
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
if (!(server->m.int_flags & NCP_IMOUNT_LOGGEDIN_POSSIBLE))
|
||||
return -EINVAL;
|
||||
if (server->root_setuped)
|
||||
return -EBUSY;
|
||||
server->root_setuped = 1;
|
||||
return ncp_conn_logged_in(inode->i_sb);
|
||||
|
||||
case NCP_IOC_GET_FS_INFO:
|
||||
return ncp_get_fs_info(server, inode, argp);
|
||||
|
||||
case NCP_IOC_GET_FS_INFO_V2:
|
||||
return ncp_get_fs_info_v2(server, inode, argp);
|
||||
|
||||
case NCP_IOC_GETMOUNTUID2:
|
||||
{
|
||||
unsigned long tmp = server->m.mounted_uid;
|
||||
|
||||
if ( (permission(inode, MAY_READ, NULL) != 0)
|
||||
&& (current->uid != server->m.mounted_uid))
|
||||
{
|
||||
return -EACCES;
|
||||
}
|
||||
if (put_user(tmp, (unsigned long __user *)argp))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
case NCP_IOC_GETROOT:
|
||||
{
|
||||
struct ncp_setroot_ioctl sr;
|
||||
|
||||
if ( (permission(inode, MAY_READ, NULL) != 0)
|
||||
&& (current->uid != server->m.mounted_uid))
|
||||
{
|
||||
return -EACCES;
|
||||
}
|
||||
if (server->m.mounted_vol[0]) {
|
||||
struct dentry* dentry = inode->i_sb->s_root;
|
||||
|
||||
if (dentry) {
|
||||
struct inode* inode = dentry->d_inode;
|
||||
|
||||
if (inode) {
|
||||
sr.volNumber = NCP_FINFO(inode)->volNumber;
|
||||
sr.dirEntNum = NCP_FINFO(inode)->dirEntNum;
|
||||
sr.namespace = server->name_space[sr.volNumber];
|
||||
} else
|
||||
DPRINTK("ncpfs: s_root->d_inode==NULL\n");
|
||||
} else
|
||||
DPRINTK("ncpfs: s_root==NULL\n");
|
||||
} else {
|
||||
sr.volNumber = -1;
|
||||
sr.namespace = 0;
|
||||
sr.dirEntNum = 0;
|
||||
}
|
||||
if (copy_to_user(argp, &sr, sizeof(sr)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case NCP_IOC_SETROOT:
|
||||
{
|
||||
struct ncp_setroot_ioctl sr;
|
||||
__u32 vnum;
|
||||
__le32 de;
|
||||
__le32 dosde;
|
||||
struct dentry* dentry;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
{
|
||||
return -EACCES;
|
||||
}
|
||||
if (server->root_setuped) return -EBUSY;
|
||||
if (copy_from_user(&sr, argp, sizeof(sr)))
|
||||
return -EFAULT;
|
||||
if (sr.volNumber < 0) {
|
||||
server->m.mounted_vol[0] = 0;
|
||||
vnum = NCP_NUMBER_OF_VOLUMES;
|
||||
de = 0;
|
||||
dosde = 0;
|
||||
} else if (sr.volNumber >= NCP_NUMBER_OF_VOLUMES) {
|
||||
return -EINVAL;
|
||||
} else if (ncp_mount_subdir(server, sr.volNumber,
|
||||
sr.namespace, sr.dirEntNum,
|
||||
&vnum, &de, &dosde)) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
dentry = inode->i_sb->s_root;
|
||||
server->root_setuped = 1;
|
||||
if (dentry) {
|
||||
struct inode* inode = dentry->d_inode;
|
||||
|
||||
if (inode) {
|
||||
NCP_FINFO(inode)->volNumber = vnum;
|
||||
NCP_FINFO(inode)->dirEntNum = de;
|
||||
NCP_FINFO(inode)->DosDirNum = dosde;
|
||||
} else
|
||||
DPRINTK("ncpfs: s_root->d_inode==NULL\n");
|
||||
} else
|
||||
DPRINTK("ncpfs: s_root==NULL\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NCPFS_PACKET_SIGNING
|
||||
case NCP_IOC_SIGN_INIT:
|
||||
if ((permission(inode, MAY_WRITE, NULL) != 0)
|
||||
&& (current->uid != server->m.mounted_uid))
|
||||
{
|
||||
return -EACCES;
|
||||
}
|
||||
if (argp) {
|
||||
if (server->sign_wanted)
|
||||
{
|
||||
struct ncp_sign_init sign;
|
||||
|
||||
if (copy_from_user(&sign, argp, sizeof(sign)))
|
||||
return -EFAULT;
|
||||
memcpy(server->sign_root,sign.sign_root,8);
|
||||
memcpy(server->sign_last,sign.sign_last,16);
|
||||
server->sign_active = 1;
|
||||
}
|
||||
/* ignore when signatures not wanted */
|
||||
} else {
|
||||
server->sign_active = 0;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case NCP_IOC_SIGN_WANTED:
|
||||
if ( (permission(inode, MAY_READ, NULL) != 0)
|
||||
&& (current->uid != server->m.mounted_uid))
|
||||
{
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (put_user(server->sign_wanted, (int __user *)argp))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
case NCP_IOC_SET_SIGN_WANTED:
|
||||
{
|
||||
int newstate;
|
||||
|
||||
if ( (permission(inode, MAY_WRITE, NULL) != 0)
|
||||
&& (current->uid != server->m.mounted_uid))
|
||||
{
|
||||
return -EACCES;
|
||||
}
|
||||
/* get only low 8 bits... */
|
||||
if (get_user(newstate, (unsigned char __user *)argp))
|
||||
return -EFAULT;
|
||||
if (server->sign_active) {
|
||||
/* cannot turn signatures OFF when active */
|
||||
if (!newstate) return -EINVAL;
|
||||
} else {
|
||||
server->sign_wanted = newstate != 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NCPFS_PACKET_SIGNING */
|
||||
|
||||
#ifdef CONFIG_NCPFS_IOCTL_LOCKING
|
||||
case NCP_IOC_LOCKUNLOCK:
|
||||
if ( (permission(inode, MAY_WRITE, NULL) != 0)
|
||||
&& (current->uid != server->m.mounted_uid))
|
||||
{
|
||||
return -EACCES;
|
||||
}
|
||||
{
|
||||
struct ncp_lock_ioctl rqdata;
|
||||
int result;
|
||||
|
||||
if (copy_from_user(&rqdata, argp, sizeof(rqdata)))
|
||||
return -EFAULT;
|
||||
if (rqdata.origin != 0)
|
||||
return -EINVAL;
|
||||
/* check for cmd */
|
||||
switch (rqdata.cmd) {
|
||||
case NCP_LOCK_EX:
|
||||
case NCP_LOCK_SH:
|
||||
if (rqdata.timeout == 0)
|
||||
rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT;
|
||||
else if (rqdata.timeout > NCP_LOCK_MAX_TIMEOUT)
|
||||
rqdata.timeout = NCP_LOCK_MAX_TIMEOUT;
|
||||
break;
|
||||
case NCP_LOCK_LOG:
|
||||
rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT; /* has no effect */
|
||||
case NCP_LOCK_CLEAR:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
/* locking needs both read and write access */
|
||||
if ((result = ncp_make_open(inode, O_RDWR)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
result = -EIO;
|
||||
if (!ncp_conn_valid(server))
|
||||
goto outrel;
|
||||
result = -EISDIR;
|
||||
if (!S_ISREG(inode->i_mode))
|
||||
goto outrel;
|
||||
if (rqdata.cmd == NCP_LOCK_CLEAR)
|
||||
{
|
||||
result = ncp_ClearPhysicalRecord(NCP_SERVER(inode),
|
||||
NCP_FINFO(inode)->file_handle,
|
||||
rqdata.offset,
|
||||
rqdata.length);
|
||||
if (result > 0) result = 0; /* no such lock */
|
||||
}
|
||||
else
|
||||
{
|
||||
int lockcmd;
|
||||
|
||||
switch (rqdata.cmd)
|
||||
{
|
||||
case NCP_LOCK_EX: lockcmd=1; break;
|
||||
case NCP_LOCK_SH: lockcmd=3; break;
|
||||
default: lockcmd=0; break;
|
||||
}
|
||||
result = ncp_LogPhysicalRecord(NCP_SERVER(inode),
|
||||
NCP_FINFO(inode)->file_handle,
|
||||
lockcmd,
|
||||
rqdata.offset,
|
||||
rqdata.length,
|
||||
rqdata.timeout);
|
||||
if (result > 0) result = -EAGAIN;
|
||||
}
|
||||
outrel:
|
||||
ncp_inode_close(inode);
|
||||
return result;
|
||||
}
|
||||
#endif /* CONFIG_NCPFS_IOCTL_LOCKING */
|
||||
|
||||
case NCP_IOC_GETOBJECTNAME:
|
||||
if (current->uid != server->m.mounted_uid) {
|
||||
return -EACCES;
|
||||
}
|
||||
{
|
||||
struct ncp_objectname_ioctl user;
|
||||
size_t outl;
|
||||
|
||||
if (copy_from_user(&user, argp, sizeof(user)))
|
||||
return -EFAULT;
|
||||
user.auth_type = server->auth.auth_type;
|
||||
outl = user.object_name_len;
|
||||
user.object_name_len = server->auth.object_name_len;
|
||||
if (outl > user.object_name_len)
|
||||
outl = user.object_name_len;
|
||||
if (outl) {
|
||||
if (copy_to_user(user.object_name,
|
||||
server->auth.object_name,
|
||||
outl)) return -EFAULT;
|
||||
}
|
||||
if (copy_to_user(argp, &user, sizeof(user)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case NCP_IOC_SETOBJECTNAME:
|
||||
if (current->uid != server->m.mounted_uid) {
|
||||
return -EACCES;
|
||||
}
|
||||
{
|
||||
struct ncp_objectname_ioctl user;
|
||||
void* newname;
|
||||
void* oldname;
|
||||
size_t oldnamelen;
|
||||
void* oldprivate;
|
||||
size_t oldprivatelen;
|
||||
|
||||
if (copy_from_user(&user, argp, sizeof(user)))
|
||||
return -EFAULT;
|
||||
if (user.object_name_len > NCP_OBJECT_NAME_MAX_LEN)
|
||||
return -ENOMEM;
|
||||
if (user.object_name_len) {
|
||||
newname = ncp_kmalloc(user.object_name_len, GFP_USER);
|
||||
if (!newname) return -ENOMEM;
|
||||
if (copy_from_user(newname, user.object_name, user.object_name_len)) {
|
||||
ncp_kfree_s(newname, user.object_name_len);
|
||||
return -EFAULT;
|
||||
}
|
||||
} else {
|
||||
newname = NULL;
|
||||
}
|
||||
/* enter critical section */
|
||||
/* maybe that kfree can sleep so do that this way */
|
||||
/* it is at least more SMP friendly (in future...) */
|
||||
oldname = server->auth.object_name;
|
||||
oldnamelen = server->auth.object_name_len;
|
||||
oldprivate = server->priv.data;
|
||||
oldprivatelen = server->priv.len;
|
||||
server->auth.auth_type = user.auth_type;
|
||||
server->auth.object_name_len = user.object_name_len;
|
||||
server->auth.object_name = newname;
|
||||
server->priv.len = 0;
|
||||
server->priv.data = NULL;
|
||||
/* leave critical section */
|
||||
if (oldprivate) ncp_kfree_s(oldprivate, oldprivatelen);
|
||||
if (oldname) ncp_kfree_s(oldname, oldnamelen);
|
||||
return 0;
|
||||
}
|
||||
case NCP_IOC_GETPRIVATEDATA:
|
||||
if (current->uid != server->m.mounted_uid) {
|
||||
return -EACCES;
|
||||
}
|
||||
{
|
||||
struct ncp_privatedata_ioctl user;
|
||||
size_t outl;
|
||||
|
||||
if (copy_from_user(&user, argp, sizeof(user)))
|
||||
return -EFAULT;
|
||||
outl = user.len;
|
||||
user.len = server->priv.len;
|
||||
if (outl > user.len) outl = user.len;
|
||||
if (outl) {
|
||||
if (copy_to_user(user.data,
|
||||
server->priv.data,
|
||||
outl)) return -EFAULT;
|
||||
}
|
||||
if (copy_to_user(argp, &user, sizeof(user)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case NCP_IOC_SETPRIVATEDATA:
|
||||
if (current->uid != server->m.mounted_uid) {
|
||||
return -EACCES;
|
||||
}
|
||||
{
|
||||
struct ncp_privatedata_ioctl user;
|
||||
void* new;
|
||||
void* old;
|
||||
size_t oldlen;
|
||||
|
||||
if (copy_from_user(&user, argp, sizeof(user)))
|
||||
return -EFAULT;
|
||||
if (user.len > NCP_PRIVATE_DATA_MAX_LEN)
|
||||
return -ENOMEM;
|
||||
if (user.len) {
|
||||
new = ncp_kmalloc(user.len, GFP_USER);
|
||||
if (!new) return -ENOMEM;
|
||||
if (copy_from_user(new, user.data, user.len)) {
|
||||
ncp_kfree_s(new, user.len);
|
||||
return -EFAULT;
|
||||
}
|
||||
} else {
|
||||
new = NULL;
|
||||
}
|
||||
/* enter critical section */
|
||||
old = server->priv.data;
|
||||
oldlen = server->priv.len;
|
||||
server->priv.len = user.len;
|
||||
server->priv.data = new;
|
||||
/* leave critical section */
|
||||
if (old) ncp_kfree_s(old, oldlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NCPFS_NLS
|
||||
case NCP_IOC_SETCHARSETS:
|
||||
return ncp_set_charsets(server, argp);
|
||||
|
||||
case NCP_IOC_GETCHARSETS:
|
||||
return ncp_get_charsets(server, argp);
|
||||
|
||||
#endif /* CONFIG_NCPFS_NLS */
|
||||
|
||||
case NCP_IOC_SETDENTRYTTL:
|
||||
if ((permission(inode, MAY_WRITE, NULL) != 0) &&
|
||||
(current->uid != server->m.mounted_uid))
|
||||
return -EACCES;
|
||||
{
|
||||
u_int32_t user;
|
||||
|
||||
if (copy_from_user(&user, argp, sizeof(user)))
|
||||
return -EFAULT;
|
||||
/* 20 secs at most... */
|
||||
if (user > 20000)
|
||||
return -EINVAL;
|
||||
user = (user * HZ) / 1000;
|
||||
server->dentry_ttl = user;
|
||||
return 0;
|
||||
}
|
||||
|
||||
case NCP_IOC_GETDENTRYTTL:
|
||||
{
|
||||
u_int32_t user = (server->dentry_ttl * 1000) / HZ;
|
||||
if (copy_to_user(argp, &user, sizeof(user)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
/* #ifdef CONFIG_UID16 */
|
||||
/* NCP_IOC_GETMOUNTUID may be same as NCP_IOC_GETMOUNTUID2,
|
||||
so we have this out of switch */
|
||||
if (cmd == NCP_IOC_GETMOUNTUID) {
|
||||
__kernel_uid_t uid = 0;
|
||||
if ((permission(inode, MAY_READ, NULL) != 0)
|
||||
&& (current->uid != server->m.mounted_uid)) {
|
||||
return -EACCES;
|
||||
}
|
||||
SET_UID(uid, server->m.mounted_uid);
|
||||
if (put_user(uid, (__kernel_uid_t __user *)argp))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
/* #endif */
|
||||
return -EINVAL;
|
||||
}
|
128
fs/ncpfs/mmap.c
Звичайний файл
128
fs/ncpfs/mmap.c
Звичайний файл
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* mmap.c
|
||||
*
|
||||
* Copyright (C) 1995, 1996 by Volker Lendecke
|
||||
* Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/stat.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/shm.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/ncp_fs.h>
|
||||
|
||||
#include "ncplib_kernel.h"
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
/*
|
||||
* Fill in the supplied page for mmap
|
||||
*/
|
||||
static struct page* ncp_file_mmap_nopage(struct vm_area_struct *area,
|
||||
unsigned long address, int *type)
|
||||
{
|
||||
struct file *file = area->vm_file;
|
||||
struct dentry *dentry = file->f_dentry;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct page* page;
|
||||
char *pg_addr;
|
||||
unsigned int already_read;
|
||||
unsigned int count;
|
||||
int bufsize;
|
||||
int pos;
|
||||
|
||||
page = alloc_page(GFP_HIGHUSER); /* ncpfs has nothing against high pages
|
||||
as long as recvmsg and memset works on it */
|
||||
if (!page)
|
||||
return page;
|
||||
pg_addr = kmap(page);
|
||||
address &= PAGE_MASK;
|
||||
pos = address - area->vm_start + (area->vm_pgoff << PAGE_SHIFT);
|
||||
|
||||
count = PAGE_SIZE;
|
||||
if (address + PAGE_SIZE > area->vm_end) {
|
||||
count = area->vm_end - address;
|
||||
}
|
||||
/* what we can read in one go */
|
||||
bufsize = NCP_SERVER(inode)->buffer_size;
|
||||
|
||||
already_read = 0;
|
||||
if (ncp_make_open(inode, O_RDONLY) >= 0) {
|
||||
while (already_read < count) {
|
||||
int read_this_time;
|
||||
int to_read;
|
||||
|
||||
to_read = bufsize - (pos % bufsize);
|
||||
|
||||
to_read = min_t(unsigned int, to_read, count - already_read);
|
||||
|
||||
if (ncp_read_kernel(NCP_SERVER(inode),
|
||||
NCP_FINFO(inode)->file_handle,
|
||||
pos, to_read,
|
||||
pg_addr + already_read,
|
||||
&read_this_time) != 0) {
|
||||
read_this_time = 0;
|
||||
}
|
||||
pos += read_this_time;
|
||||
already_read += read_this_time;
|
||||
|
||||
if (read_this_time < to_read) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ncp_inode_close(inode);
|
||||
|
||||
}
|
||||
|
||||
if (already_read < PAGE_SIZE)
|
||||
memset(pg_addr + already_read, 0, PAGE_SIZE - already_read);
|
||||
flush_dcache_page(page);
|
||||
kunmap(page);
|
||||
|
||||
/*
|
||||
* If I understand ncp_read_kernel() properly, the above always
|
||||
* fetches from the network, here the analogue of disk.
|
||||
* -- wli
|
||||
*/
|
||||
if (type)
|
||||
*type = VM_FAULT_MAJOR;
|
||||
inc_page_state(pgmajfault);
|
||||
return page;
|
||||
}
|
||||
|
||||
static struct vm_operations_struct ncp_file_mmap =
|
||||
{
|
||||
.nopage = ncp_file_mmap_nopage,
|
||||
};
|
||||
|
||||
|
||||
/* This is used for a general mmap of a ncp file */
|
||||
int ncp_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct inode *inode = file->f_dentry->d_inode;
|
||||
|
||||
DPRINTK("ncp_mmap: called\n");
|
||||
|
||||
if (!ncp_conn_valid(NCP_SERVER(inode)))
|
||||
return -EIO;
|
||||
|
||||
/* only PAGE_COW or read-only supported now */
|
||||
if (vma->vm_flags & VM_SHARED)
|
||||
return -EINVAL;
|
||||
/* we do not support files bigger than 4GB... We eventually
|
||||
supports just 4GB... */
|
||||
if (((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff
|
||||
> (1U << (32 - PAGE_SHIFT)))
|
||||
return -EFBIG;
|
||||
|
||||
vma->vm_ops = &ncp_file_mmap;
|
||||
file_accessed(file);
|
||||
return 0;
|
||||
}
|
1355
fs/ncpfs/ncplib_kernel.c
Звичайний файл
1355
fs/ncpfs/ncplib_kernel.c
Звичайний файл
Різницю між файлами не показано, бо вона завелика
Завантажити різницю
259
fs/ncpfs/ncplib_kernel.h
Звичайний файл
259
fs/ncpfs/ncplib_kernel.h
Звичайний файл
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* ncplib_kernel.h
|
||||
*
|
||||
* Copyright (C) 1995, 1996 by Volker Lendecke
|
||||
* Modified for big endian by J.F. Chadima and David S. Miller
|
||||
* Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
|
||||
* Modified 1998, 1999 Wolfram Pienkoss for NLS
|
||||
* Modified 1999 Wolfram Pienkoss for directory caching
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _NCPLIB_H
|
||||
#define _NCPLIB_H
|
||||
|
||||
#include <linux/config.h>
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/pagemap.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <asm/string.h>
|
||||
|
||||
#ifdef CONFIG_NCPFS_NLS
|
||||
#include <linux/nls.h>
|
||||
#else
|
||||
#include <linux/ctype.h>
|
||||
#endif /* CONFIG_NCPFS_NLS */
|
||||
|
||||
#include <linux/ncp_fs.h>
|
||||
|
||||
#define NCP_MIN_SYMLINK_SIZE 8
|
||||
#define NCP_MAX_SYMLINK_SIZE 512
|
||||
|
||||
#define NCP_BLOCK_SHIFT 9
|
||||
#define NCP_BLOCK_SIZE (1 << (NCP_BLOCK_SHIFT))
|
||||
|
||||
int ncp_negotiate_buffersize(struct ncp_server *, int, int *);
|
||||
int ncp_negotiate_size_and_options(struct ncp_server *server, int size,
|
||||
int options, int *ret_size, int *ret_options);
|
||||
|
||||
int ncp_get_volume_info_with_number(struct ncp_server* server, int n,
|
||||
struct ncp_volume_info *target);
|
||||
|
||||
int ncp_get_directory_info(struct ncp_server* server, __u8 dirhandle,
|
||||
struct ncp_volume_info* target);
|
||||
|
||||
int ncp_close_file(struct ncp_server *, const char *);
|
||||
static inline int ncp_read_bounce_size(__u32 size) {
|
||||
return sizeof(struct ncp_reply_header) + 2 + 2 + size + 8;
|
||||
};
|
||||
int ncp_read_bounce(struct ncp_server *, const char *, __u32, __u16,
|
||||
char __user *, int *, void* bounce, __u32 bouncelen);
|
||||
int ncp_read_kernel(struct ncp_server *, const char *, __u32, __u16,
|
||||
char *, int *);
|
||||
int ncp_write_kernel(struct ncp_server *, const char *, __u32, __u16,
|
||||
const char *, int *);
|
||||
|
||||
static inline void ncp_inode_close(struct inode *inode) {
|
||||
atomic_dec(&NCP_FINFO(inode)->opened);
|
||||
}
|
||||
|
||||
void ncp_extract_file_info(void* src, struct nw_info_struct* target);
|
||||
int ncp_obtain_info(struct ncp_server *server, struct inode *, char *,
|
||||
struct nw_info_struct *target);
|
||||
int ncp_obtain_nfs_info(struct ncp_server *server, struct nw_info_struct *target);
|
||||
int ncp_get_volume_root(struct ncp_server *server, const char *volname,
|
||||
__u32 *volume, __le32 *dirent, __le32 *dosdirent);
|
||||
int ncp_lookup_volume(struct ncp_server *, const char *, struct nw_info_struct *);
|
||||
int ncp_modify_file_or_subdir_dos_info(struct ncp_server *, struct inode *,
|
||||
__le32, const struct nw_modify_dos_info *info);
|
||||
int ncp_modify_file_or_subdir_dos_info_path(struct ncp_server *, struct inode *,
|
||||
const char* path, __le32, const struct nw_modify_dos_info *info);
|
||||
int ncp_modify_nfs_info(struct ncp_server *, __u8 volnum, __le32 dirent,
|
||||
__u32 mode, __u32 rdev);
|
||||
|
||||
int ncp_del_file_or_subdir2(struct ncp_server *, struct dentry*);
|
||||
int ncp_del_file_or_subdir(struct ncp_server *, struct inode *, char *);
|
||||
int ncp_open_create_file_or_subdir(struct ncp_server *, struct inode *, char *,
|
||||
int, __le32, __le16, struct ncp_entry_info *);
|
||||
|
||||
int ncp_initialize_search(struct ncp_server *, struct inode *,
|
||||
struct nw_search_sequence *target);
|
||||
int ncp_search_for_file_or_subdir(struct ncp_server *server,
|
||||
struct nw_search_sequence *seq,
|
||||
struct nw_info_struct *target);
|
||||
int ncp_search_for_fileset(struct ncp_server *server,
|
||||
struct nw_search_sequence *seq,
|
||||
int* more, int* cnt,
|
||||
char* buffer, size_t bufsize,
|
||||
char** rbuf, size_t* rsize);
|
||||
|
||||
int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
|
||||
struct inode *, char *, struct inode *, char *);
|
||||
|
||||
|
||||
int
|
||||
ncp_LogPhysicalRecord(struct ncp_server *server,
|
||||
const char *file_id, __u8 locktype,
|
||||
__u32 offset, __u32 length, __u16 timeout);
|
||||
|
||||
#ifdef CONFIG_NCPFS_IOCTL_LOCKING
|
||||
int
|
||||
ncp_ClearPhysicalRecord(struct ncp_server *server,
|
||||
const char *file_id,
|
||||
__u32 offset, __u32 length);
|
||||
#endif /* CONFIG_NCPFS_IOCTL_LOCKING */
|
||||
|
||||
int
|
||||
ncp_mount_subdir(struct ncp_server *, __u8, __u8, __le32,
|
||||
__u32* volume, __le32* dirent, __le32* dosdirent);
|
||||
int ncp_dirhandle_alloc(struct ncp_server *, __u8 vol, __le32 dirent, __u8 *dirhandle);
|
||||
int ncp_dirhandle_free(struct ncp_server *, __u8 dirhandle);
|
||||
|
||||
int ncp_create_new(struct inode *dir, struct dentry *dentry,
|
||||
int mode, dev_t rdev, __le32 attributes);
|
||||
|
||||
static inline int ncp_is_nfs_extras(struct ncp_server* server, unsigned int volnum) {
|
||||
#ifdef CONFIG_NCPFS_NFS_NS
|
||||
return (server->m.flags & NCP_MOUNT_NFS_EXTRAS) &&
|
||||
(server->name_space[volnum] == NW_NS_NFS);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NCPFS_NLS
|
||||
|
||||
int ncp__io2vol(struct ncp_server *, unsigned char *, unsigned int *,
|
||||
const unsigned char *, unsigned int, int);
|
||||
int ncp__vol2io(struct ncp_server *, unsigned char *, unsigned int *,
|
||||
const unsigned char *, unsigned int, int);
|
||||
|
||||
#define NCP_ESC ':'
|
||||
#define NCP_IO_TABLE(dentry) (NCP_SERVER((dentry)->d_inode)->nls_io)
|
||||
#define ncp_tolower(t, c) nls_tolower(t, c)
|
||||
#define ncp_toupper(t, c) nls_toupper(t, c)
|
||||
#define ncp_strnicmp(t, s1, s2, len) \
|
||||
nls_strnicmp(t, s1, s2, len)
|
||||
#define ncp_io2vol(S,m,i,n,k,U) ncp__io2vol(S,m,i,n,k,U)
|
||||
#define ncp_vol2io(S,m,i,n,k,U) ncp__vol2io(S,m,i,n,k,U)
|
||||
|
||||
#else
|
||||
|
||||
int ncp__io2vol(unsigned char *, unsigned int *,
|
||||
const unsigned char *, unsigned int, int);
|
||||
int ncp__vol2io(unsigned char *, unsigned int *,
|
||||
const unsigned char *, unsigned int, int);
|
||||
|
||||
#define NCP_IO_TABLE(dentry) NULL
|
||||
#define ncp_tolower(t, c) tolower(c)
|
||||
#define ncp_toupper(t, c) toupper(c)
|
||||
#define ncp_io2vol(S,m,i,n,k,U) ncp__io2vol(m,i,n,k,U)
|
||||
#define ncp_vol2io(S,m,i,n,k,U) ncp__vol2io(m,i,n,k,U)
|
||||
|
||||
|
||||
static inline int ncp_strnicmp(struct nls_table *t, const unsigned char *s1,
|
||||
const unsigned char *s2, int len)
|
||||
{
|
||||
while (len--) {
|
||||
if (tolower(*s1++) != tolower(*s2++))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NCPFS_NLS */
|
||||
|
||||
#define NCP_GET_AGE(dentry) (jiffies - (dentry)->d_time)
|
||||
#define NCP_MAX_AGE(server) ((server)->dentry_ttl)
|
||||
#define NCP_TEST_AGE(server,dentry) (NCP_GET_AGE(dentry) < NCP_MAX_AGE(server))
|
||||
|
||||
static inline void
|
||||
ncp_age_dentry(struct ncp_server* server, struct dentry* dentry)
|
||||
{
|
||||
dentry->d_time = jiffies - server->dentry_ttl;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ncp_new_dentry(struct dentry* dentry)
|
||||
{
|
||||
dentry->d_time = jiffies;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ncp_renew_dentries(struct dentry *parent)
|
||||
{
|
||||
struct ncp_server *server = NCP_SERVER(parent->d_inode);
|
||||
struct list_head *next;
|
||||
struct dentry *dentry;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
next = parent->d_subdirs.next;
|
||||
while (next != &parent->d_subdirs) {
|
||||
dentry = list_entry(next, struct dentry, d_child);
|
||||
|
||||
if (dentry->d_fsdata == NULL)
|
||||
ncp_age_dentry(server, dentry);
|
||||
else
|
||||
ncp_new_dentry(dentry);
|
||||
|
||||
next = next->next;
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
}
|
||||
|
||||
static inline void
|
||||
ncp_invalidate_dircache_entries(struct dentry *parent)
|
||||
{
|
||||
struct ncp_server *server = NCP_SERVER(parent->d_inode);
|
||||
struct list_head *next;
|
||||
struct dentry *dentry;
|
||||
|
||||
spin_lock(&dcache_lock);
|
||||
next = parent->d_subdirs.next;
|
||||
while (next != &parent->d_subdirs) {
|
||||
dentry = list_entry(next, struct dentry, d_child);
|
||||
dentry->d_fsdata = NULL;
|
||||
ncp_age_dentry(server, dentry);
|
||||
next = next->next;
|
||||
}
|
||||
spin_unlock(&dcache_lock);
|
||||
}
|
||||
|
||||
struct ncp_cache_head {
|
||||
time_t mtime;
|
||||
unsigned long time; /* cache age */
|
||||
unsigned long end; /* last valid fpos in cache */
|
||||
int eof;
|
||||
};
|
||||
|
||||
#define NCP_DIRCACHE_SIZE ((int)(PAGE_CACHE_SIZE/sizeof(struct dentry *)))
|
||||
union ncp_dir_cache {
|
||||
struct ncp_cache_head head;
|
||||
struct dentry *dentry[NCP_DIRCACHE_SIZE];
|
||||
};
|
||||
|
||||
#define NCP_FIRSTCACHE_SIZE ((int)((NCP_DIRCACHE_SIZE * \
|
||||
sizeof(struct dentry *) - sizeof(struct ncp_cache_head)) / \
|
||||
sizeof(struct dentry *)))
|
||||
|
||||
#define NCP_DIRCACHE_START (NCP_DIRCACHE_SIZE - NCP_FIRSTCACHE_SIZE)
|
||||
|
||||
struct ncp_cache_control {
|
||||
struct ncp_cache_head head;
|
||||
struct page *page;
|
||||
union ncp_dir_cache *cache;
|
||||
unsigned long fpos, ofs;
|
||||
int filled, valid, idx;
|
||||
};
|
||||
|
||||
#endif /* _NCPLIB_H */
|
127
fs/ncpfs/ncpsign_kernel.c
Звичайний файл
127
fs/ncpfs/ncpsign_kernel.c
Звичайний файл
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* ncpsign_kernel.c
|
||||
*
|
||||
* Arne de Bruijn (arne@knoware.nl), 1997
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
|
||||
#ifdef CONFIG_NCPFS_PACKET_SIGNING
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/ncp.h>
|
||||
#include <linux/bitops.h>
|
||||
#include "ncpsign_kernel.h"
|
||||
|
||||
/* i386: 32-bit, little endian, handles mis-alignment */
|
||||
#ifdef __i386__
|
||||
#define GET_LE32(p) (*(int *)(p))
|
||||
#define PUT_LE32(p,v) { *(int *)(p)=v; }
|
||||
#else
|
||||
/* from include/ncplib.h */
|
||||
#define BVAL(buf,pos) (((__u8 *)(buf))[pos])
|
||||
#define PVAL(buf,pos) ((unsigned)BVAL(buf,pos))
|
||||
#define BSET(buf,pos,val) (BVAL(buf,pos) = (val))
|
||||
|
||||
static inline __u16
|
||||
WVAL_LH(__u8 * buf, int pos)
|
||||
{
|
||||
return PVAL(buf, pos) | PVAL(buf, pos + 1) << 8;
|
||||
}
|
||||
static inline __u32
|
||||
DVAL_LH(__u8 * buf, int pos)
|
||||
{
|
||||
return WVAL_LH(buf, pos) | WVAL_LH(buf, pos + 2) << 16;
|
||||
}
|
||||
static inline void
|
||||
WSET_LH(__u8 * buf, int pos, __u16 val)
|
||||
{
|
||||
BSET(buf, pos, val & 0xff);
|
||||
BSET(buf, pos + 1, val >> 8);
|
||||
}
|
||||
static inline void
|
||||
DSET_LH(__u8 * buf, int pos, __u32 val)
|
||||
{
|
||||
WSET_LH(buf, pos, val & 0xffff);
|
||||
WSET_LH(buf, pos + 2, val >> 16);
|
||||
}
|
||||
|
||||
#define GET_LE32(p) DVAL_LH(p,0)
|
||||
#define PUT_LE32(p,v) DSET_LH(p,0,v)
|
||||
#endif
|
||||
|
||||
static void nwsign(char *r_data1, char *r_data2, char *outdata) {
|
||||
int i;
|
||||
unsigned int w0,w1,w2,w3;
|
||||
static int rbit[4]={0, 2, 1, 3};
|
||||
#ifdef __i386__
|
||||
unsigned int *data2=(int *)r_data2;
|
||||
#else
|
||||
unsigned int data2[16];
|
||||
for (i=0;i<16;i++)
|
||||
data2[i]=GET_LE32(r_data2+(i<<2));
|
||||
#endif
|
||||
w0=GET_LE32(r_data1);
|
||||
w1=GET_LE32(r_data1+4);
|
||||
w2=GET_LE32(r_data1+8);
|
||||
w3=GET_LE32(r_data1+12);
|
||||
for (i=0;i<16;i+=4) {
|
||||
w0=rol32(w0 + ((w1 & w2) | ((~w1) & w3)) + data2[i+0],3);
|
||||
w3=rol32(w3 + ((w0 & w1) | ((~w0) & w2)) + data2[i+1],7);
|
||||
w2=rol32(w2 + ((w3 & w0) | ((~w3) & w1)) + data2[i+2],11);
|
||||
w1=rol32(w1 + ((w2 & w3) | ((~w2) & w0)) + data2[i+3],19);
|
||||
}
|
||||
for (i=0;i<4;i++) {
|
||||
w0=rol32(w0 + (((w2 | w3) & w1) | (w2 & w3)) + 0x5a827999 + data2[i+0],3);
|
||||
w3=rol32(w3 + (((w1 | w2) & w0) | (w1 & w2)) + 0x5a827999 + data2[i+4],5);
|
||||
w2=rol32(w2 + (((w0 | w1) & w3) | (w0 & w1)) + 0x5a827999 + data2[i+8],9);
|
||||
w1=rol32(w1 + (((w3 | w0) & w2) | (w3 & w0)) + 0x5a827999 + data2[i+12],13);
|
||||
}
|
||||
for (i=0;i<4;i++) {
|
||||
w0=rol32(w0 + ((w1 ^ w2) ^ w3) + 0x6ed9eba1 + data2[rbit[i]+0],3);
|
||||
w3=rol32(w3 + ((w0 ^ w1) ^ w2) + 0x6ed9eba1 + data2[rbit[i]+8],9);
|
||||
w2=rol32(w2 + ((w3 ^ w0) ^ w1) + 0x6ed9eba1 + data2[rbit[i]+4],11);
|
||||
w1=rol32(w1 + ((w2 ^ w3) ^ w0) + 0x6ed9eba1 + data2[rbit[i]+12],15);
|
||||
}
|
||||
PUT_LE32(outdata,(w0+GET_LE32(r_data1)) & 0xffffffff);
|
||||
PUT_LE32(outdata+4,(w1+GET_LE32(r_data1+4)) & 0xffffffff);
|
||||
PUT_LE32(outdata+8,(w2+GET_LE32(r_data1+8)) & 0xffffffff);
|
||||
PUT_LE32(outdata+12,(w3+GET_LE32(r_data1+12)) & 0xffffffff);
|
||||
}
|
||||
|
||||
/* Make a signature for the current packet and add it at the end of the */
|
||||
/* packet. */
|
||||
void __sign_packet(struct ncp_server *server, const char *packet, size_t size, __u32 totalsize, void *sign_buff) {
|
||||
unsigned char data[64];
|
||||
|
||||
memcpy(data, server->sign_root, 8);
|
||||
*(__u32*)(data + 8) = totalsize;
|
||||
if (size < 52) {
|
||||
memcpy(data + 12, packet, size);
|
||||
memset(data + 12 + size, 0, 52 - size);
|
||||
} else {
|
||||
memcpy(data + 12, packet, 52);
|
||||
}
|
||||
nwsign(server->sign_last, data, server->sign_last);
|
||||
memcpy(sign_buff, server->sign_last, 8);
|
||||
}
|
||||
|
||||
int sign_verify_reply(struct ncp_server *server, const char *packet, size_t size, __u32 totalsize, const void *sign_buff) {
|
||||
unsigned char data[64];
|
||||
unsigned char hash[16];
|
||||
|
||||
memcpy(data, server->sign_root, 8);
|
||||
*(__u32*)(data + 8) = totalsize;
|
||||
if (size < 52) {
|
||||
memcpy(data + 12, packet, size);
|
||||
memset(data + 12 + size, 0, 52 - size);
|
||||
} else {
|
||||
memcpy(data + 12, packet, 52);
|
||||
}
|
||||
nwsign(server->sign_last, data, hash);
|
||||
return memcmp(sign_buff, hash, 8);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NCPFS_PACKET_SIGNING */
|
||||
|
28
fs/ncpfs/ncpsign_kernel.h
Звичайний файл
28
fs/ncpfs/ncpsign_kernel.h
Звичайний файл
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* ncpsign_kernel.h
|
||||
*
|
||||
* Arne de Bruijn (arne@knoware.nl), 1997
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _NCPSIGN_KERNEL_H
|
||||
#define _NCPSIGN_KERNEL_H
|
||||
|
||||
#include <linux/ncp_fs.h>
|
||||
|
||||
#ifdef CONFIG_NCPFS_PACKET_SIGNING
|
||||
void __sign_packet(struct ncp_server *server, const char *data, size_t size, __u32 totalsize, void *sign_buff);
|
||||
int sign_verify_reply(struct ncp_server *server, const char *data, size_t size, __u32 totalsize, const void *sign_buff);
|
||||
#endif
|
||||
|
||||
static inline size_t sign_packet(struct ncp_server *server, const char *data, size_t size, __u32 totalsize, void *sign_buff) {
|
||||
#ifdef CONFIG_NCPFS_PACKET_SIGNING
|
||||
if (server->sign_active) {
|
||||
__sign_packet(server, data, size, totalsize, sign_buff);
|
||||
return 8;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
850
fs/ncpfs/sock.c
Звичайний файл
850
fs/ncpfs/sock.c
Звичайний файл
@@ -0,0 +1,850 @@
|
||||
/*
|
||||
* linux/fs/ncpfs/sock.c
|
||||
*
|
||||
* Copyright (C) 1992, 1993 Rick Sladkey
|
||||
*
|
||||
* Modified 1995, 1996 by Volker Lendecke to be usable for ncp
|
||||
* Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
|
||||
#include <linux/time.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/stat.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/signal.h>
|
||||
#include <net/scm.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/ipx.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/file.h>
|
||||
|
||||
#include <linux/ncp_fs.h>
|
||||
|
||||
#include "ncpsign_kernel.h"
|
||||
|
||||
static int _recv(struct socket *sock, void *buf, int size, unsigned flags)
|
||||
{
|
||||
struct msghdr msg = {NULL, };
|
||||
struct kvec iov = {buf, size};
|
||||
return kernel_recvmsg(sock, &msg, &iov, 1, size, flags);
|
||||
}
|
||||
|
||||
static inline int do_send(struct socket *sock, struct kvec *vec, int count,
|
||||
int len, unsigned flags)
|
||||
{
|
||||
struct msghdr msg = { .msg_flags = flags };
|
||||
return kernel_sendmsg(sock, &msg, vec, count, len);
|
||||
}
|
||||
|
||||
static int _send(struct socket *sock, const void *buff, int len)
|
||||
{
|
||||
struct kvec vec;
|
||||
vec.iov_base = (void *) buff;
|
||||
vec.iov_len = len;
|
||||
return do_send(sock, &vec, 1, len, 0);
|
||||
}
|
||||
|
||||
struct ncp_request_reply {
|
||||
struct list_head req;
|
||||
wait_queue_head_t wq;
|
||||
struct ncp_reply_header* reply_buf;
|
||||
size_t datalen;
|
||||
int result;
|
||||
enum { RQ_DONE, RQ_INPROGRESS, RQ_QUEUED, RQ_IDLE } status;
|
||||
struct kvec* tx_ciov;
|
||||
size_t tx_totallen;
|
||||
size_t tx_iovlen;
|
||||
struct kvec tx_iov[3];
|
||||
u_int16_t tx_type;
|
||||
u_int32_t sign[6];
|
||||
};
|
||||
|
||||
void ncp_tcp_data_ready(struct sock *sk, int len)
|
||||
{
|
||||
struct ncp_server *server = sk->sk_user_data;
|
||||
|
||||
server->data_ready(sk, len);
|
||||
schedule_work(&server->rcv.tq);
|
||||
}
|
||||
|
||||
void ncp_tcp_error_report(struct sock *sk)
|
||||
{
|
||||
struct ncp_server *server = sk->sk_user_data;
|
||||
|
||||
server->error_report(sk);
|
||||
schedule_work(&server->rcv.tq);
|
||||
}
|
||||
|
||||
void ncp_tcp_write_space(struct sock *sk)
|
||||
{
|
||||
struct ncp_server *server = sk->sk_user_data;
|
||||
|
||||
/* We do not need any locking: we first set tx.creq, and then we do sendmsg,
|
||||
not vice versa... */
|
||||
server->write_space(sk);
|
||||
if (server->tx.creq)
|
||||
schedule_work(&server->tx.tq);
|
||||
}
|
||||
|
||||
void ncpdgram_timeout_call(unsigned long v)
|
||||
{
|
||||
struct ncp_server *server = (void*)v;
|
||||
|
||||
schedule_work(&server->timeout_tq);
|
||||
}
|
||||
|
||||
static inline void ncp_finish_request(struct ncp_request_reply *req, int result)
|
||||
{
|
||||
req->result = result;
|
||||
req->status = RQ_DONE;
|
||||
wake_up_all(&req->wq);
|
||||
}
|
||||
|
||||
static void __abort_ncp_connection(struct ncp_server *server, struct ncp_request_reply *aborted, int err)
|
||||
{
|
||||
struct ncp_request_reply *req;
|
||||
|
||||
ncp_invalidate_conn(server);
|
||||
del_timer(&server->timeout_tm);
|
||||
while (!list_empty(&server->tx.requests)) {
|
||||
req = list_entry(server->tx.requests.next, struct ncp_request_reply, req);
|
||||
|
||||
list_del_init(&req->req);
|
||||
if (req == aborted) {
|
||||
ncp_finish_request(req, err);
|
||||
} else {
|
||||
ncp_finish_request(req, -EIO);
|
||||
}
|
||||
}
|
||||
req = server->rcv.creq;
|
||||
if (req) {
|
||||
server->rcv.creq = NULL;
|
||||
if (req == aborted) {
|
||||
ncp_finish_request(req, err);
|
||||
} else {
|
||||
ncp_finish_request(req, -EIO);
|
||||
}
|
||||
server->rcv.ptr = NULL;
|
||||
server->rcv.state = 0;
|
||||
}
|
||||
req = server->tx.creq;
|
||||
if (req) {
|
||||
server->tx.creq = NULL;
|
||||
if (req == aborted) {
|
||||
ncp_finish_request(req, err);
|
||||
} else {
|
||||
ncp_finish_request(req, -EIO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline int get_conn_number(struct ncp_reply_header *rp)
|
||||
{
|
||||
return rp->conn_low | (rp->conn_high << 8);
|
||||
}
|
||||
|
||||
static inline void __ncp_abort_request(struct ncp_server *server, struct ncp_request_reply *req, int err)
|
||||
{
|
||||
/* If req is done, we got signal, but we also received answer... */
|
||||
switch (req->status) {
|
||||
case RQ_IDLE:
|
||||
case RQ_DONE:
|
||||
break;
|
||||
case RQ_QUEUED:
|
||||
list_del_init(&req->req);
|
||||
ncp_finish_request(req, err);
|
||||
break;
|
||||
case RQ_INPROGRESS:
|
||||
__abort_ncp_connection(server, req, err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ncp_abort_request(struct ncp_server *server, struct ncp_request_reply *req, int err)
|
||||
{
|
||||
down(&server->rcv.creq_sem);
|
||||
__ncp_abort_request(server, req, err);
|
||||
up(&server->rcv.creq_sem);
|
||||
}
|
||||
|
||||
static inline void __ncptcp_abort(struct ncp_server *server)
|
||||
{
|
||||
__abort_ncp_connection(server, NULL, 0);
|
||||
}
|
||||
|
||||
static int ncpdgram_send(struct socket *sock, struct ncp_request_reply *req)
|
||||
{
|
||||
struct kvec vec[3];
|
||||
/* sock_sendmsg updates iov pointers for us :-( */
|
||||
memcpy(vec, req->tx_ciov, req->tx_iovlen * sizeof(vec[0]));
|
||||
return do_send(sock, vec, req->tx_iovlen,
|
||||
req->tx_totallen, MSG_DONTWAIT);
|
||||
}
|
||||
|
||||
static void __ncptcp_try_send(struct ncp_server *server)
|
||||
{
|
||||
struct ncp_request_reply *rq;
|
||||
struct kvec *iov;
|
||||
struct kvec iovc[3];
|
||||
int result;
|
||||
|
||||
rq = server->tx.creq;
|
||||
if (!rq)
|
||||
return;
|
||||
|
||||
/* sock_sendmsg updates iov pointers for us :-( */
|
||||
memcpy(iovc, rq->tx_ciov, rq->tx_iovlen * sizeof(iov[0]));
|
||||
result = do_send(server->ncp_sock, iovc, rq->tx_iovlen,
|
||||
rq->tx_totallen, MSG_NOSIGNAL | MSG_DONTWAIT);
|
||||
|
||||
if (result == -EAGAIN)
|
||||
return;
|
||||
|
||||
if (result < 0) {
|
||||
printk(KERN_ERR "ncpfs: tcp: Send failed: %d\n", result);
|
||||
__ncp_abort_request(server, rq, result);
|
||||
return;
|
||||
}
|
||||
if (result >= rq->tx_totallen) {
|
||||
server->rcv.creq = rq;
|
||||
server->tx.creq = NULL;
|
||||
return;
|
||||
}
|
||||
rq->tx_totallen -= result;
|
||||
iov = rq->tx_ciov;
|
||||
while (iov->iov_len <= result) {
|
||||
result -= iov->iov_len;
|
||||
iov++;
|
||||
rq->tx_iovlen--;
|
||||
}
|
||||
iov->iov_base += result;
|
||||
iov->iov_len -= result;
|
||||
rq->tx_ciov = iov;
|
||||
}
|
||||
|
||||
static inline void ncp_init_header(struct ncp_server *server, struct ncp_request_reply *req, struct ncp_request_header *h)
|
||||
{
|
||||
req->status = RQ_INPROGRESS;
|
||||
h->conn_low = server->connection;
|
||||
h->conn_high = server->connection >> 8;
|
||||
h->sequence = ++server->sequence;
|
||||
}
|
||||
|
||||
static void ncpdgram_start_request(struct ncp_server *server, struct ncp_request_reply *req)
|
||||
{
|
||||
size_t signlen;
|
||||
struct ncp_request_header* h;
|
||||
|
||||
req->tx_ciov = req->tx_iov + 1;
|
||||
|
||||
h = req->tx_iov[1].iov_base;
|
||||
ncp_init_header(server, req, h);
|
||||
signlen = sign_packet(server, req->tx_iov[1].iov_base + sizeof(struct ncp_request_header) - 1,
|
||||
req->tx_iov[1].iov_len - sizeof(struct ncp_request_header) + 1,
|
||||
cpu_to_le32(req->tx_totallen), req->sign);
|
||||
if (signlen) {
|
||||
req->tx_ciov[1].iov_base = req->sign;
|
||||
req->tx_ciov[1].iov_len = signlen;
|
||||
req->tx_iovlen += 1;
|
||||
req->tx_totallen += signlen;
|
||||
}
|
||||
server->rcv.creq = req;
|
||||
server->timeout_last = server->m.time_out;
|
||||
server->timeout_retries = server->m.retry_count;
|
||||
ncpdgram_send(server->ncp_sock, req);
|
||||
mod_timer(&server->timeout_tm, jiffies + server->m.time_out);
|
||||
}
|
||||
|
||||
#define NCP_TCP_XMIT_MAGIC (0x446D6454)
|
||||
#define NCP_TCP_XMIT_VERSION (1)
|
||||
#define NCP_TCP_RCVD_MAGIC (0x744E6350)
|
||||
|
||||
static void ncptcp_start_request(struct ncp_server *server, struct ncp_request_reply *req)
|
||||
{
|
||||
size_t signlen;
|
||||
struct ncp_request_header* h;
|
||||
|
||||
req->tx_ciov = req->tx_iov;
|
||||
h = req->tx_iov[1].iov_base;
|
||||
ncp_init_header(server, req, h);
|
||||
signlen = sign_packet(server, req->tx_iov[1].iov_base + sizeof(struct ncp_request_header) - 1,
|
||||
req->tx_iov[1].iov_len - sizeof(struct ncp_request_header) + 1,
|
||||
cpu_to_be32(req->tx_totallen + 24), req->sign + 4) + 16;
|
||||
|
||||
req->sign[0] = htonl(NCP_TCP_XMIT_MAGIC);
|
||||
req->sign[1] = htonl(req->tx_totallen + signlen);
|
||||
req->sign[2] = htonl(NCP_TCP_XMIT_VERSION);
|
||||
req->sign[3] = htonl(req->datalen + 8);
|
||||
req->tx_iov[0].iov_base = req->sign;
|
||||
req->tx_iov[0].iov_len = signlen;
|
||||
req->tx_iovlen += 1;
|
||||
req->tx_totallen += signlen;
|
||||
|
||||
server->tx.creq = req;
|
||||
__ncptcp_try_send(server);
|
||||
}
|
||||
|
||||
static inline void __ncp_start_request(struct ncp_server *server, struct ncp_request_reply *req)
|
||||
{
|
||||
if (server->ncp_sock->type == SOCK_STREAM)
|
||||
ncptcp_start_request(server, req);
|
||||
else
|
||||
ncpdgram_start_request(server, req);
|
||||
}
|
||||
|
||||
static int ncp_add_request(struct ncp_server *server, struct ncp_request_reply *req)
|
||||
{
|
||||
down(&server->rcv.creq_sem);
|
||||
if (!ncp_conn_valid(server)) {
|
||||
up(&server->rcv.creq_sem);
|
||||
printk(KERN_ERR "ncpfs: tcp: Server died\n");
|
||||
return -EIO;
|
||||
}
|
||||
if (server->tx.creq || server->rcv.creq) {
|
||||
req->status = RQ_QUEUED;
|
||||
list_add_tail(&req->req, &server->tx.requests);
|
||||
up(&server->rcv.creq_sem);
|
||||
return 0;
|
||||
}
|
||||
__ncp_start_request(server, req);
|
||||
up(&server->rcv.creq_sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __ncp_next_request(struct ncp_server *server)
|
||||
{
|
||||
struct ncp_request_reply *req;
|
||||
|
||||
server->rcv.creq = NULL;
|
||||
if (list_empty(&server->tx.requests)) {
|
||||
return;
|
||||
}
|
||||
req = list_entry(server->tx.requests.next, struct ncp_request_reply, req);
|
||||
list_del_init(&req->req);
|
||||
__ncp_start_request(server, req);
|
||||
}
|
||||
|
||||
static void info_server(struct ncp_server *server, unsigned int id, const void * data, size_t len)
|
||||
{
|
||||
if (server->info_sock) {
|
||||
struct kvec iov[2];
|
||||
__be32 hdr[2];
|
||||
|
||||
hdr[0] = cpu_to_be32(len + 8);
|
||||
hdr[1] = cpu_to_be32(id);
|
||||
|
||||
iov[0].iov_base = hdr;
|
||||
iov[0].iov_len = 8;
|
||||
iov[1].iov_base = (void *) data;
|
||||
iov[1].iov_len = len;
|
||||
|
||||
do_send(server->info_sock, iov, 2, len + 8, MSG_NOSIGNAL);
|
||||
}
|
||||
}
|
||||
|
||||
void ncpdgram_rcv_proc(void *s)
|
||||
{
|
||||
struct ncp_server *server = s;
|
||||
struct socket* sock;
|
||||
|
||||
sock = server->ncp_sock;
|
||||
|
||||
while (1) {
|
||||
struct ncp_reply_header reply;
|
||||
int result;
|
||||
|
||||
result = _recv(sock, &reply, sizeof(reply), MSG_PEEK | MSG_DONTWAIT);
|
||||
if (result < 0) {
|
||||
break;
|
||||
}
|
||||
if (result >= sizeof(reply)) {
|
||||
struct ncp_request_reply *req;
|
||||
|
||||
if (reply.type == NCP_WATCHDOG) {
|
||||
unsigned char buf[10];
|
||||
|
||||
if (server->connection != get_conn_number(&reply)) {
|
||||
goto drop;
|
||||
}
|
||||
result = _recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
|
||||
if (result < 0) {
|
||||
DPRINTK("recv failed with %d\n", result);
|
||||
continue;
|
||||
}
|
||||
if (result < 10) {
|
||||
DPRINTK("too short (%u) watchdog packet\n", result);
|
||||
continue;
|
||||
}
|
||||
if (buf[9] != '?') {
|
||||
DPRINTK("bad signature (%02X) in watchdog packet\n", buf[9]);
|
||||
continue;
|
||||
}
|
||||
buf[9] = 'Y';
|
||||
_send(sock, buf, sizeof(buf));
|
||||
continue;
|
||||
}
|
||||
if (reply.type != NCP_POSITIVE_ACK && reply.type != NCP_REPLY) {
|
||||
result = _recv(sock, server->unexpected_packet.data, sizeof(server->unexpected_packet.data), MSG_DONTWAIT);
|
||||
if (result < 0) {
|
||||
continue;
|
||||
}
|
||||
info_server(server, 0, server->unexpected_packet.data, result);
|
||||
continue;
|
||||
}
|
||||
down(&server->rcv.creq_sem);
|
||||
req = server->rcv.creq;
|
||||
if (req && (req->tx_type == NCP_ALLOC_SLOT_REQUEST || (server->sequence == reply.sequence &&
|
||||
server->connection == get_conn_number(&reply)))) {
|
||||
if (reply.type == NCP_POSITIVE_ACK) {
|
||||
server->timeout_retries = server->m.retry_count;
|
||||
server->timeout_last = NCP_MAX_RPC_TIMEOUT;
|
||||
mod_timer(&server->timeout_tm, jiffies + NCP_MAX_RPC_TIMEOUT);
|
||||
} else if (reply.type == NCP_REPLY) {
|
||||
result = _recv(sock, (void*)req->reply_buf, req->datalen, MSG_DONTWAIT);
|
||||
#ifdef CONFIG_NCPFS_PACKET_SIGNING
|
||||
if (result >= 0 && server->sign_active && req->tx_type != NCP_DEALLOC_SLOT_REQUEST) {
|
||||
if (result < 8 + 8) {
|
||||
result = -EIO;
|
||||
} else {
|
||||
unsigned int hdrl;
|
||||
|
||||
result -= 8;
|
||||
hdrl = sock->sk->sk_family == AF_INET ? 8 : 6;
|
||||
if (sign_verify_reply(server, ((char*)req->reply_buf) + hdrl, result - hdrl, cpu_to_le32(result), ((char*)req->reply_buf) + result)) {
|
||||
printk(KERN_INFO "ncpfs: Signature violation\n");
|
||||
result = -EIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
del_timer(&server->timeout_tm);
|
||||
server->rcv.creq = NULL;
|
||||
ncp_finish_request(req, result);
|
||||
__ncp_next_request(server);
|
||||
up(&server->rcv.creq_sem);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
up(&server->rcv.creq_sem);
|
||||
}
|
||||
drop:;
|
||||
_recv(sock, &reply, sizeof(reply), MSG_DONTWAIT);
|
||||
}
|
||||
}
|
||||
|
||||
static void __ncpdgram_timeout_proc(struct ncp_server *server)
|
||||
{
|
||||
/* If timer is pending, we are processing another request... */
|
||||
if (!timer_pending(&server->timeout_tm)) {
|
||||
struct ncp_request_reply* req;
|
||||
|
||||
req = server->rcv.creq;
|
||||
if (req) {
|
||||
int timeout;
|
||||
|
||||
if (server->m.flags & NCP_MOUNT_SOFT) {
|
||||
if (server->timeout_retries-- == 0) {
|
||||
__ncp_abort_request(server, req, -ETIMEDOUT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Ignore errors */
|
||||
ncpdgram_send(server->ncp_sock, req);
|
||||
timeout = server->timeout_last << 1;
|
||||
if (timeout > NCP_MAX_RPC_TIMEOUT) {
|
||||
timeout = NCP_MAX_RPC_TIMEOUT;
|
||||
}
|
||||
server->timeout_last = timeout;
|
||||
mod_timer(&server->timeout_tm, jiffies + timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ncpdgram_timeout_proc(void *s)
|
||||
{
|
||||
struct ncp_server *server = s;
|
||||
down(&server->rcv.creq_sem);
|
||||
__ncpdgram_timeout_proc(server);
|
||||
up(&server->rcv.creq_sem);
|
||||
}
|
||||
|
||||
static inline void ncp_init_req(struct ncp_request_reply* req)
|
||||
{
|
||||
init_waitqueue_head(&req->wq);
|
||||
req->status = RQ_IDLE;
|
||||
}
|
||||
|
||||
static int do_tcp_rcv(struct ncp_server *server, void *buffer, size_t len)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (buffer) {
|
||||
result = _recv(server->ncp_sock, buffer, len, MSG_DONTWAIT);
|
||||
} else {
|
||||
static unsigned char dummy[1024];
|
||||
|
||||
if (len > sizeof(dummy)) {
|
||||
len = sizeof(dummy);
|
||||
}
|
||||
result = _recv(server->ncp_sock, dummy, len, MSG_DONTWAIT);
|
||||
}
|
||||
if (result < 0) {
|
||||
return result;
|
||||
}
|
||||
if (result > len) {
|
||||
printk(KERN_ERR "ncpfs: tcp: bug in recvmsg (%u > %Zu)\n", result, len);
|
||||
return -EIO;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int __ncptcp_rcv_proc(struct ncp_server *server)
|
||||
{
|
||||
/* We have to check the result, so store the complete header */
|
||||
while (1) {
|
||||
int result;
|
||||
struct ncp_request_reply *req;
|
||||
int datalen;
|
||||
int type;
|
||||
|
||||
while (server->rcv.len) {
|
||||
result = do_tcp_rcv(server, server->rcv.ptr, server->rcv.len);
|
||||
if (result == -EAGAIN) {
|
||||
return 0;
|
||||
}
|
||||
if (result <= 0) {
|
||||
req = server->rcv.creq;
|
||||
if (req) {
|
||||
__ncp_abort_request(server, req, -EIO);
|
||||
} else {
|
||||
__ncptcp_abort(server);
|
||||
}
|
||||
if (result < 0) {
|
||||
printk(KERN_ERR "ncpfs: tcp: error in recvmsg: %d\n", result);
|
||||
} else {
|
||||
DPRINTK(KERN_ERR "ncpfs: tcp: EOF\n");
|
||||
}
|
||||
return -EIO;
|
||||
}
|
||||
if (server->rcv.ptr) {
|
||||
server->rcv.ptr += result;
|
||||
}
|
||||
server->rcv.len -= result;
|
||||
}
|
||||
switch (server->rcv.state) {
|
||||
case 0:
|
||||
if (server->rcv.buf.magic != htonl(NCP_TCP_RCVD_MAGIC)) {
|
||||
printk(KERN_ERR "ncpfs: tcp: Unexpected reply type %08X\n", ntohl(server->rcv.buf.magic));
|
||||
__ncptcp_abort(server);
|
||||
return -EIO;
|
||||
}
|
||||
datalen = ntohl(server->rcv.buf.len) & 0x0FFFFFFF;
|
||||
if (datalen < 10) {
|
||||
printk(KERN_ERR "ncpfs: tcp: Unexpected reply len %d\n", datalen);
|
||||
__ncptcp_abort(server);
|
||||
return -EIO;
|
||||
}
|
||||
#ifdef CONFIG_NCPFS_PACKET_SIGNING
|
||||
if (server->sign_active) {
|
||||
if (datalen < 18) {
|
||||
printk(KERN_ERR "ncpfs: tcp: Unexpected reply len %d\n", datalen);
|
||||
__ncptcp_abort(server);
|
||||
return -EIO;
|
||||
}
|
||||
server->rcv.buf.len = datalen - 8;
|
||||
server->rcv.ptr = (unsigned char*)&server->rcv.buf.p1;
|
||||
server->rcv.len = 8;
|
||||
server->rcv.state = 4;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
type = ntohs(server->rcv.buf.type);
|
||||
#ifdef CONFIG_NCPFS_PACKET_SIGNING
|
||||
cont:;
|
||||
#endif
|
||||
if (type != NCP_REPLY) {
|
||||
if (datalen - 8 <= sizeof(server->unexpected_packet.data)) {
|
||||
*(__u16*)(server->unexpected_packet.data) = htons(type);
|
||||
server->unexpected_packet.len = datalen - 8;
|
||||
|
||||
server->rcv.state = 5;
|
||||
server->rcv.ptr = server->unexpected_packet.data + 2;
|
||||
server->rcv.len = datalen - 10;
|
||||
break;
|
||||
}
|
||||
DPRINTK("ncpfs: tcp: Unexpected NCP type %02X\n", type);
|
||||
skipdata2:;
|
||||
server->rcv.state = 2;
|
||||
skipdata:;
|
||||
server->rcv.ptr = NULL;
|
||||
server->rcv.len = datalen - 10;
|
||||
break;
|
||||
}
|
||||
req = server->rcv.creq;
|
||||
if (!req) {
|
||||
DPRINTK(KERN_ERR "ncpfs: Reply without appropriate request\n");
|
||||
goto skipdata2;
|
||||
}
|
||||
if (datalen > req->datalen + 8) {
|
||||
printk(KERN_ERR "ncpfs: tcp: Unexpected reply len %d (expected at most %Zd)\n", datalen, req->datalen + 8);
|
||||
server->rcv.state = 3;
|
||||
goto skipdata;
|
||||
}
|
||||
req->datalen = datalen - 8;
|
||||
req->reply_buf->type = NCP_REPLY;
|
||||
server->rcv.ptr = (unsigned char*)(req->reply_buf) + 2;
|
||||
server->rcv.len = datalen - 10;
|
||||
server->rcv.state = 1;
|
||||
break;
|
||||
#ifdef CONFIG_NCPFS_PACKET_SIGNING
|
||||
case 4:
|
||||
datalen = server->rcv.buf.len;
|
||||
type = ntohs(server->rcv.buf.type2);
|
||||
goto cont;
|
||||
#endif
|
||||
case 1:
|
||||
req = server->rcv.creq;
|
||||
if (req->tx_type != NCP_ALLOC_SLOT_REQUEST) {
|
||||
if (req->reply_buf->sequence != server->sequence) {
|
||||
printk(KERN_ERR "ncpfs: tcp: Bad sequence number\n");
|
||||
__ncp_abort_request(server, req, -EIO);
|
||||
return -EIO;
|
||||
}
|
||||
if ((req->reply_buf->conn_low | (req->reply_buf->conn_high << 8)) != server->connection) {
|
||||
printk(KERN_ERR "ncpfs: tcp: Connection number mismatch\n");
|
||||
__ncp_abort_request(server, req, -EIO);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_NCPFS_PACKET_SIGNING
|
||||
if (server->sign_active && req->tx_type != NCP_DEALLOC_SLOT_REQUEST) {
|
||||
if (sign_verify_reply(server, (unsigned char*)(req->reply_buf) + 6, req->datalen - 6, cpu_to_be32(req->datalen + 16), &server->rcv.buf.type)) {
|
||||
printk(KERN_ERR "ncpfs: tcp: Signature violation\n");
|
||||
__ncp_abort_request(server, req, -EIO);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
ncp_finish_request(req, req->datalen);
|
||||
nextreq:;
|
||||
__ncp_next_request(server);
|
||||
case 2:
|
||||
next:;
|
||||
server->rcv.ptr = (unsigned char*)&server->rcv.buf;
|
||||
server->rcv.len = 10;
|
||||
server->rcv.state = 0;
|
||||
break;
|
||||
case 3:
|
||||
ncp_finish_request(server->rcv.creq, -EIO);
|
||||
goto nextreq;
|
||||
case 5:
|
||||
info_server(server, 0, server->unexpected_packet.data, server->unexpected_packet.len);
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ncp_tcp_rcv_proc(void *s)
|
||||
{
|
||||
struct ncp_server *server = s;
|
||||
|
||||
down(&server->rcv.creq_sem);
|
||||
__ncptcp_rcv_proc(server);
|
||||
up(&server->rcv.creq_sem);
|
||||
}
|
||||
|
||||
void ncp_tcp_tx_proc(void *s)
|
||||
{
|
||||
struct ncp_server *server = s;
|
||||
|
||||
down(&server->rcv.creq_sem);
|
||||
__ncptcp_try_send(server);
|
||||
up(&server->rcv.creq_sem);
|
||||
}
|
||||
|
||||
static int do_ncp_rpc_call(struct ncp_server *server, int size,
|
||||
struct ncp_reply_header* reply_buf, int max_reply_size)
|
||||
{
|
||||
int result;
|
||||
struct ncp_request_reply req;
|
||||
|
||||
ncp_init_req(&req);
|
||||
req.reply_buf = reply_buf;
|
||||
req.datalen = max_reply_size;
|
||||
req.tx_iov[1].iov_base = server->packet;
|
||||
req.tx_iov[1].iov_len = size;
|
||||
req.tx_iovlen = 1;
|
||||
req.tx_totallen = size;
|
||||
req.tx_type = *(u_int16_t*)server->packet;
|
||||
|
||||
result = ncp_add_request(server, &req);
|
||||
if (result < 0) {
|
||||
return result;
|
||||
}
|
||||
if (wait_event_interruptible(req.wq, req.status == RQ_DONE)) {
|
||||
ncp_abort_request(server, &req, -EIO);
|
||||
}
|
||||
return req.result;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need the server to be locked here, so check!
|
||||
*/
|
||||
|
||||
static int ncp_do_request(struct ncp_server *server, int size,
|
||||
void* reply, int max_reply_size)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (server->lock == 0) {
|
||||
printk(KERN_ERR "ncpfs: Server not locked!\n");
|
||||
return -EIO;
|
||||
}
|
||||
if (!ncp_conn_valid(server)) {
|
||||
printk(KERN_ERR "ncpfs: Connection invalid!\n");
|
||||
return -EIO;
|
||||
}
|
||||
{
|
||||
sigset_t old_set;
|
||||
unsigned long mask, flags;
|
||||
|
||||
spin_lock_irqsave(¤t->sighand->siglock, flags);
|
||||
old_set = current->blocked;
|
||||
if (current->flags & PF_EXITING)
|
||||
mask = 0;
|
||||
else
|
||||
mask = sigmask(SIGKILL);
|
||||
if (server->m.flags & NCP_MOUNT_INTR) {
|
||||
/* FIXME: This doesn't seem right at all. So, like,
|
||||
we can't handle SIGINT and get whatever to stop?
|
||||
What if we've blocked it ourselves? What about
|
||||
alarms? Why, in fact, are we mucking with the
|
||||
sigmask at all? -- r~ */
|
||||
if (current->sighand->action[SIGINT - 1].sa.sa_handler == SIG_DFL)
|
||||
mask |= sigmask(SIGINT);
|
||||
if (current->sighand->action[SIGQUIT - 1].sa.sa_handler == SIG_DFL)
|
||||
mask |= sigmask(SIGQUIT);
|
||||
}
|
||||
siginitsetinv(¤t->blocked, mask);
|
||||
recalc_sigpending();
|
||||
spin_unlock_irqrestore(¤t->sighand->siglock, flags);
|
||||
|
||||
result = do_ncp_rpc_call(server, size, reply, max_reply_size);
|
||||
|
||||
spin_lock_irqsave(¤t->sighand->siglock, flags);
|
||||
current->blocked = old_set;
|
||||
recalc_sigpending();
|
||||
spin_unlock_irqrestore(¤t->sighand->siglock, flags);
|
||||
}
|
||||
|
||||
DDPRINTK("do_ncp_rpc_call returned %d\n", result);
|
||||
|
||||
if (result < 0) {
|
||||
/* There was a problem with I/O, so the connections is
|
||||
* no longer usable. */
|
||||
ncp_invalidate_conn(server);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ncp_do_request assures that at least a complete reply header is
|
||||
* received. It assumes that server->current_size contains the ncp
|
||||
* request size
|
||||
*/
|
||||
int ncp_request2(struct ncp_server *server, int function,
|
||||
void* rpl, int size)
|
||||
{
|
||||
struct ncp_request_header *h;
|
||||
struct ncp_reply_header* reply = rpl;
|
||||
int result;
|
||||
|
||||
h = (struct ncp_request_header *) (server->packet);
|
||||
if (server->has_subfunction != 0) {
|
||||
*(__u16 *) & (h->data[0]) = htons(server->current_size - sizeof(*h) - 2);
|
||||
}
|
||||
h->type = NCP_REQUEST;
|
||||
/*
|
||||
* The server shouldn't know or care what task is making a
|
||||
* request, so we always use the same task number.
|
||||
*/
|
||||
h->task = 2; /* (current->pid) & 0xff; */
|
||||
h->function = function;
|
||||
|
||||
result = ncp_do_request(server, server->current_size, reply, size);
|
||||
if (result < 0) {
|
||||
DPRINTK("ncp_request_error: %d\n", result);
|
||||
goto out;
|
||||
}
|
||||
server->completion = reply->completion_code;
|
||||
server->conn_status = reply->connection_state;
|
||||
server->reply_size = result;
|
||||
server->ncp_reply_size = result - sizeof(struct ncp_reply_header);
|
||||
|
||||
result = reply->completion_code;
|
||||
|
||||
if (result != 0)
|
||||
PPRINTK("ncp_request: completion code=%x\n", result);
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
int ncp_connect(struct ncp_server *server)
|
||||
{
|
||||
struct ncp_request_header *h;
|
||||
int result;
|
||||
|
||||
server->connection = 0xFFFF;
|
||||
server->sequence = 255;
|
||||
|
||||
h = (struct ncp_request_header *) (server->packet);
|
||||
h->type = NCP_ALLOC_SLOT_REQUEST;
|
||||
h->task = 2; /* see above */
|
||||
h->function = 0;
|
||||
|
||||
result = ncp_do_request(server, sizeof(*h), server->packet, server->packet_size);
|
||||
if (result < 0)
|
||||
goto out;
|
||||
server->connection = h->conn_low + (h->conn_high * 256);
|
||||
result = 0;
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
int ncp_disconnect(struct ncp_server *server)
|
||||
{
|
||||
struct ncp_request_header *h;
|
||||
|
||||
h = (struct ncp_request_header *) (server->packet);
|
||||
h->type = NCP_DEALLOC_SLOT_REQUEST;
|
||||
h->task = 2; /* see above */
|
||||
h->function = 0;
|
||||
|
||||
return ncp_do_request(server, sizeof(*h), server->packet, server->packet_size);
|
||||
}
|
||||
|
||||
void ncp_lock_server(struct ncp_server *server)
|
||||
{
|
||||
down(&server->sem);
|
||||
if (server->lock)
|
||||
printk(KERN_WARNING "ncp_lock_server: was locked!\n");
|
||||
server->lock = 1;
|
||||
}
|
||||
|
||||
void ncp_unlock_server(struct ncp_server *server)
|
||||
{
|
||||
if (!server->lock) {
|
||||
printk(KERN_WARNING "ncp_unlock_server: was not locked!\n");
|
||||
return;
|
||||
}
|
||||
server->lock = 0;
|
||||
up(&server->sem);
|
||||
}
|
183
fs/ncpfs/symlink.c
Звичайний файл
183
fs/ncpfs/symlink.c
Звичайний файл
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* linux/fs/ncpfs/symlink.c
|
||||
*
|
||||
* Code for allowing symbolic links on NCPFS (i.e. NetWare)
|
||||
* Symbolic links are not supported on native NetWare, so we use an
|
||||
* infrequently-used flag (Sh) and store a two-word magic header in
|
||||
* the file to make sure we don't accidentally use a non-link file
|
||||
* as a link.
|
||||
*
|
||||
* When using the NFS namespace, we set the mode to indicate a symlink and
|
||||
* don't bother with the magic numbers.
|
||||
*
|
||||
* from linux/fs/ext2/symlink.c
|
||||
*
|
||||
* Copyright (C) 1998-99, Frank A. Vorstenbosch
|
||||
*
|
||||
* ncpfs symlink handling code
|
||||
* NLS support (c) 1999 Petr Vandrovec
|
||||
* Modified 2000 Ben Harris, University of Cambridge for NFS NS meta-info
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/ncp_fs.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/stat.h>
|
||||
#include "ncplib_kernel.h"
|
||||
|
||||
|
||||
/* these magic numbers must appear in the symlink file -- this makes it a bit
|
||||
more resilient against the magic attributes being set on random files. */
|
||||
|
||||
#define NCP_SYMLINK_MAGIC0 cpu_to_le32(0x6c6d7973) /* "symlnk->" */
|
||||
#define NCP_SYMLINK_MAGIC1 cpu_to_le32(0x3e2d6b6e)
|
||||
|
||||
/* ----- read a symbolic link ------------------------------------------ */
|
||||
|
||||
static int ncp_symlink_readpage(struct file *file, struct page *page)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
int error, length, len;
|
||||
char *link, *rawlink;
|
||||
char *buf = kmap(page);
|
||||
|
||||
error = -ENOMEM;
|
||||
rawlink=(char *)kmalloc(NCP_MAX_SYMLINK_SIZE, GFP_KERNEL);
|
||||
if (!rawlink)
|
||||
goto fail;
|
||||
|
||||
if (ncp_make_open(inode,O_RDONLY))
|
||||
goto failEIO;
|
||||
|
||||
error=ncp_read_kernel(NCP_SERVER(inode),NCP_FINFO(inode)->file_handle,
|
||||
0,NCP_MAX_SYMLINK_SIZE,rawlink,&length);
|
||||
|
||||
ncp_inode_close(inode);
|
||||
/* Close file handle if no other users... */
|
||||
ncp_make_closed(inode);
|
||||
if (error)
|
||||
goto failEIO;
|
||||
|
||||
if (NCP_FINFO(inode)->flags & NCPI_KLUDGE_SYMLINK) {
|
||||
if (length<NCP_MIN_SYMLINK_SIZE ||
|
||||
((__le32 *)rawlink)[0]!=NCP_SYMLINK_MAGIC0 ||
|
||||
((__le32 *)rawlink)[1]!=NCP_SYMLINK_MAGIC1)
|
||||
goto failEIO;
|
||||
link = rawlink + 8;
|
||||
length -= 8;
|
||||
} else {
|
||||
link = rawlink;
|
||||
}
|
||||
|
||||
len = NCP_MAX_SYMLINK_SIZE;
|
||||
error = ncp_vol2io(NCP_SERVER(inode), buf, &len, link, length, 0);
|
||||
kfree(rawlink);
|
||||
if (error)
|
||||
goto fail;
|
||||
SetPageUptodate(page);
|
||||
kunmap(page);
|
||||
unlock_page(page);
|
||||
return 0;
|
||||
|
||||
failEIO:
|
||||
error = -EIO;
|
||||
kfree(rawlink);
|
||||
fail:
|
||||
SetPageError(page);
|
||||
kunmap(page);
|
||||
unlock_page(page);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* symlinks can't do much...
|
||||
*/
|
||||
struct address_space_operations ncp_symlink_aops = {
|
||||
.readpage = ncp_symlink_readpage,
|
||||
};
|
||||
|
||||
/* ----- create a new symbolic link -------------------------------------- */
|
||||
|
||||
int ncp_symlink(struct inode *dir, struct dentry *dentry, const char *symname) {
|
||||
struct inode *inode;
|
||||
char *rawlink;
|
||||
int length, err, i, outlen;
|
||||
int kludge;
|
||||
int mode;
|
||||
__le32 attr;
|
||||
unsigned int hdr;
|
||||
|
||||
DPRINTK("ncp_symlink(dir=%p,dentry=%p,symname=%s)\n",dir,dentry,symname);
|
||||
|
||||
if (ncp_is_nfs_extras(NCP_SERVER(dir), NCP_FINFO(dir)->volNumber))
|
||||
kludge = 0;
|
||||
else
|
||||
#ifdef CONFIG_NCPFS_EXTRAS
|
||||
if (NCP_SERVER(dir)->m.flags & NCP_MOUNT_SYMLINKS)
|
||||
kludge = 1;
|
||||
else
|
||||
#endif
|
||||
/* EPERM is returned by VFS if symlink procedure does not exist */
|
||||
return -EPERM;
|
||||
|
||||
rawlink=(char *)kmalloc(NCP_MAX_SYMLINK_SIZE, GFP_KERNEL);
|
||||
if (!rawlink)
|
||||
return -ENOMEM;
|
||||
|
||||
if (kludge) {
|
||||
mode = 0;
|
||||
attr = aSHARED | aHIDDEN;
|
||||
((__le32 *)rawlink)[0]=NCP_SYMLINK_MAGIC0;
|
||||
((__le32 *)rawlink)[1]=NCP_SYMLINK_MAGIC1;
|
||||
hdr = 8;
|
||||
} else {
|
||||
mode = S_IFLNK | S_IRWXUGO;
|
||||
attr = 0;
|
||||
hdr = 0;
|
||||
}
|
||||
|
||||
length = strlen(symname);
|
||||
/* map to/from server charset, do not touch upper/lower case as
|
||||
symlink can point out of ncp filesystem */
|
||||
outlen = NCP_MAX_SYMLINK_SIZE - hdr;
|
||||
err = ncp_io2vol(NCP_SERVER(dir), rawlink + hdr, &outlen, symname, length, 0);
|
||||
if (err)
|
||||
goto failfree;
|
||||
|
||||
outlen += hdr;
|
||||
|
||||
err = -EIO;
|
||||
if (ncp_create_new(dir,dentry,mode,0,attr)) {
|
||||
goto failfree;
|
||||
}
|
||||
|
||||
inode=dentry->d_inode;
|
||||
|
||||
if (ncp_make_open(inode, O_WRONLY))
|
||||
goto failfree;
|
||||
|
||||
if (ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
|
||||
0, outlen, rawlink, &i) || i!=outlen) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ncp_inode_close(inode);
|
||||
ncp_make_closed(inode);
|
||||
kfree(rawlink);
|
||||
return 0;
|
||||
fail:;
|
||||
ncp_inode_close(inode);
|
||||
ncp_make_closed(inode);
|
||||
failfree:;
|
||||
kfree(rawlink);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* ----- EOF ----- */
|
Посилання в новій задачі
Заблокувати користувача