/*
|
/*
|
* linux/fs/nfs/proc.c
|
* linux/fs/nfs/proc.c
|
*
|
*
|
* Copyright (C) 1992, 1993, 1994 Rick Sladkey
|
* Copyright (C) 1992, 1993, 1994 Rick Sladkey
|
*
|
*
|
* OS-independent nfs remote procedure call functions
|
* OS-independent nfs remote procedure call functions
|
*
|
*
|
* Tuned by Alan Cox <A.Cox@swansea.ac.uk> for >3K buffers
|
* Tuned by Alan Cox <A.Cox@swansea.ac.uk> for >3K buffers
|
* so at last we can have decent(ish) throughput off a
|
* so at last we can have decent(ish) throughput off a
|
* Sun server.
|
* Sun server.
|
*
|
*
|
* Coding optimized and cleaned up by Florian La Roche.
|
* Coding optimized and cleaned up by Florian La Roche.
|
* Note: Error returns are optimized for NFS_OK, which isn't translated via
|
* Note: Error returns are optimized for NFS_OK, which isn't translated via
|
* nfs_stat_to_errno(), but happens to be already the right return code.
|
* nfs_stat_to_errno(), but happens to be already the right return code.
|
*
|
*
|
* FixMe: We ought to define a sensible small max size for
|
* FixMe: We ought to define a sensible small max size for
|
* things like getattr that are tiny packets and use the
|
* things like getattr that are tiny packets and use the
|
* old get_free_page stuff with it.
|
* old get_free_page stuff with it.
|
*
|
*
|
* Also, the code currently doesn't check the size of the packet, when
|
* Also, the code currently doesn't check the size of the packet, when
|
* it decodes the packet.
|
* it decodes the packet.
|
*
|
*
|
* Feel free to fix it and mail me the diffs if it worries you.
|
* Feel free to fix it and mail me the diffs if it worries you.
|
*/
|
*/
|
|
|
/*
|
/*
|
* Fixes:
|
* Fixes:
|
* Ion Badulescu <ionut@cs.columbia.edu> : FIFO's need special handling in NFSv2
|
* Ion Badulescu <ionut@cs.columbia.edu> : FIFO's need special handling in NFSv2
|
*/
|
*/
|
|
|
/*
|
/*
|
* Defining NFS_PROC_DEBUG causes a lookup of a file named
|
* Defining NFS_PROC_DEBUG causes a lookup of a file named
|
* "xyzzy" to toggle debugging. Just cd to an NFS-mounted
|
* "xyzzy" to toggle debugging. Just cd to an NFS-mounted
|
* filesystem and type 'ls xyzzy' to turn on debugging.
|
* filesystem and type 'ls xyzzy' to turn on debugging.
|
*/
|
*/
|
|
|
#if 0
|
#if 0
|
#define NFS_PROC_DEBUG
|
#define NFS_PROC_DEBUG
|
#endif
|
#endif
|
|
|
#include <linux/param.h>
|
#include <linux/param.h>
|
#include <linux/sched.h>
|
#include <linux/sched.h>
|
#include <linux/mm.h>
|
#include <linux/mm.h>
|
#include <linux/malloc.h>
|
#include <linux/malloc.h>
|
#include <linux/nfs_fs.h>
|
#include <linux/nfs_fs.h>
|
#include <linux/utsname.h>
|
#include <linux/utsname.h>
|
#include <linux/errno.h>
|
#include <linux/errno.h>
|
#include <linux/string.h>
|
#include <linux/string.h>
|
#include <linux/in.h>
|
#include <linux/in.h>
|
#include <linux/pagemap.h>
|
#include <linux/pagemap.h>
|
|
|
#include <asm/segment.h>
|
#include <asm/segment.h>
|
|
|
#ifdef NFS_PROC_DEBUG
|
#ifdef NFS_PROC_DEBUG
|
|
|
static int proc_debug = 0;
|
static int proc_debug = 0;
|
#define PRINTK(format, args...) \
|
#define PRINTK(format, args...) \
|
do { \
|
do { \
|
if (proc_debug) \
|
if (proc_debug) \
|
printk(format , ## args); \
|
printk(format , ## args); \
|
} while (0)
|
} while (0)
|
|
|
#else /* !NFS_PROC_DEBUG */
|
#else /* !NFS_PROC_DEBUG */
|
|
|
#define PRINTK(format, args...) do ; while (0)
|
#define PRINTK(format, args...) do ; while (0)
|
|
|
#endif /* !NFS_PROC_DEBUG */
|
#endif /* !NFS_PROC_DEBUG */
|
|
|
/* Mapping from NFS error code to "errno" error code. */
|
/* Mapping from NFS error code to "errno" error code. */
|
#define errno_NFSERR_IO EIO
|
#define errno_NFSERR_IO EIO
|
|
|
static int *nfs_rpc_header(int *p, int procedure, int ruid);
|
static int *nfs_rpc_header(int *p, int procedure, int ruid);
|
static int *nfs_rpc_verify(int *p);
|
static int *nfs_rpc_verify(int *p);
|
static int nfs_stat_to_errno(int stat);
|
static int nfs_stat_to_errno(int stat);
|
|
|
/*
|
/*
|
* Our memory allocation and release functions.
|
* Our memory allocation and release functions.
|
*/
|
*/
|
|
|
#define NFS_SLACK_SPACE 1024 /* Total overkill */
|
#define NFS_SLACK_SPACE 1024 /* Total overkill */
|
/* !!! Be careful, this constant is now also used in sock.c...
|
/* !!! Be careful, this constant is now also used in sock.c...
|
We should easily convert to not using it anymore for most cases... */
|
We should easily convert to not using it anymore for most cases... */
|
|
|
static inline int *nfs_rpc_alloc(int size)
|
static inline int *nfs_rpc_alloc(int size)
|
{
|
{
|
int *i;
|
int *i;
|
|
|
while (!(i = (int *)kmalloc(size+NFS_SLACK_SPACE,GFP_NFS))) {
|
while (!(i = (int *)kmalloc(size+NFS_SLACK_SPACE,GFP_NFS))) {
|
schedule();
|
schedule();
|
}
|
}
|
return i;
|
return i;
|
}
|
}
|
|
|
static inline void nfs_rpc_free(int *p)
|
static inline void nfs_rpc_free(int *p)
|
{
|
{
|
kfree((void *)p);
|
kfree((void *)p);
|
}
|
}
|
|
|
/*
|
/*
|
* Here are a bunch of xdr encode/decode functions that convert
|
* Here are a bunch of xdr encode/decode functions that convert
|
* between machine dependent and xdr data formats.
|
* between machine dependent and xdr data formats.
|
*/
|
*/
|
|
|
#define QUADLEN(len) (((len) + 3) >> 2)
|
#define QUADLEN(len) (((len) + 3) >> 2)
|
|
|
static inline int *xdr_encode_fhandle(int *p, struct nfs_fh *fhandle)
|
static inline int *xdr_encode_fhandle(int *p, struct nfs_fh *fhandle)
|
{
|
{
|
*((struct nfs_fh *) p) = *fhandle;
|
*((struct nfs_fh *) p) = *fhandle;
|
return p + QUADLEN(sizeof(*fhandle));
|
return p + QUADLEN(sizeof(*fhandle));
|
}
|
}
|
|
|
static inline int *xdr_decode_fhandle(int *p, struct nfs_fh *fhandle)
|
static inline int *xdr_decode_fhandle(int *p, struct nfs_fh *fhandle)
|
{
|
{
|
*fhandle = *((struct nfs_fh *) p);
|
*fhandle = *((struct nfs_fh *) p);
|
return p + QUADLEN(sizeof(*fhandle));
|
return p + QUADLEN(sizeof(*fhandle));
|
}
|
}
|
|
|
static inline int *xdr_encode_string(int *p, const char *string)
|
static inline int *xdr_encode_string(int *p, const char *string)
|
{
|
{
|
int len = strlen(string);
|
int len = strlen(string);
|
int quadlen = QUADLEN(len);
|
int quadlen = QUADLEN(len);
|
|
|
p[quadlen] = 0;
|
p[quadlen] = 0;
|
*p++ = htonl(len);
|
*p++ = htonl(len);
|
memcpy(p, string, len);
|
memcpy(p, string, len);
|
return p + quadlen;
|
return p + quadlen;
|
}
|
}
|
|
|
static inline int *xdr_decode_string(int *p, char *string, unsigned int maxlen)
|
static inline int *xdr_decode_string(int *p, char *string, unsigned int maxlen)
|
{
|
{
|
unsigned int len = ntohl(*p++);
|
unsigned int len = ntohl(*p++);
|
if (len > maxlen)
|
if (len > maxlen)
|
return NULL;
|
return NULL;
|
memcpy(string, p, len);
|
memcpy(string, p, len);
|
string[len] = '\0';
|
string[len] = '\0';
|
return p + QUADLEN(len);
|
return p + QUADLEN(len);
|
}
|
}
|
|
|
static inline int *xdr_decode_string2(int *p, char **string, unsigned int *len,
|
static inline int *xdr_decode_string2(int *p, char **string, unsigned int *len,
|
unsigned int maxlen)
|
unsigned int maxlen)
|
{
|
{
|
*len = ntohl(*p++);
|
*len = ntohl(*p++);
|
if (*len > maxlen)
|
if (*len > maxlen)
|
return NULL;
|
return NULL;
|
*string = (char *) p;
|
*string = (char *) p;
|
return p + QUADLEN(*len);
|
return p + QUADLEN(*len);
|
}
|
}
|
|
|
|
|
static inline int *xdr_encode_data(int *p, const char *data, int len)
|
static inline int *xdr_encode_data(int *p, const char *data, int len)
|
{
|
{
|
int quadlen = QUADLEN(len);
|
int quadlen = QUADLEN(len);
|
|
|
p[quadlen] = 0;
|
p[quadlen] = 0;
|
*p++ = htonl(len);
|
*p++ = htonl(len);
|
memcpy_fromfs(p, data, len);
|
memcpy_fromfs(p, data, len);
|
return p + quadlen;
|
return p + quadlen;
|
}
|
}
|
|
|
static inline int *xdr_decode_data(int *p, char *data, int *lenp, int maxlen)
|
static inline int *xdr_decode_data(int *p, char *data, int *lenp, int maxlen)
|
{
|
{
|
unsigned len = *lenp = ntohl(*p++);
|
unsigned len = *lenp = ntohl(*p++);
|
if (len > maxlen)
|
if (len > maxlen)
|
return NULL;
|
return NULL;
|
memcpy(data, p, len);
|
memcpy(data, p, len);
|
return p + QUADLEN(len);
|
return p + QUADLEN(len);
|
}
|
}
|
|
|
static int *xdr_decode_fattr(int *p, struct nfs_fattr *fattr)
|
static int *xdr_decode_fattr(int *p, struct nfs_fattr *fattr)
|
{
|
{
|
fattr->type = (enum nfs_ftype) ntohl(*p++);
|
fattr->type = (enum nfs_ftype) ntohl(*p++);
|
fattr->mode = ntohl(*p++);
|
fattr->mode = ntohl(*p++);
|
fattr->nlink = ntohl(*p++);
|
fattr->nlink = ntohl(*p++);
|
fattr->uid = ntohl(*p++);
|
fattr->uid = ntohl(*p++);
|
fattr->gid = ntohl(*p++);
|
fattr->gid = ntohl(*p++);
|
fattr->size = ntohl(*p++);
|
fattr->size = ntohl(*p++);
|
fattr->blocksize = ntohl(*p++);
|
fattr->blocksize = ntohl(*p++);
|
fattr->rdev = ntohl(*p++);
|
fattr->rdev = ntohl(*p++);
|
fattr->blocks = ntohl(*p++);
|
fattr->blocks = ntohl(*p++);
|
fattr->fsid = ntohl(*p++);
|
fattr->fsid = ntohl(*p++);
|
fattr->fileid = ntohl(*p++);
|
fattr->fileid = ntohl(*p++);
|
fattr->atime.seconds = ntohl(*p++);
|
fattr->atime.seconds = ntohl(*p++);
|
fattr->atime.useconds = ntohl(*p++);
|
fattr->atime.useconds = ntohl(*p++);
|
fattr->mtime.seconds = ntohl(*p++);
|
fattr->mtime.seconds = ntohl(*p++);
|
fattr->mtime.useconds = ntohl(*p++);
|
fattr->mtime.useconds = ntohl(*p++);
|
fattr->ctime.seconds = ntohl(*p++);
|
fattr->ctime.seconds = ntohl(*p++);
|
fattr->ctime.useconds = ntohl(*p++);
|
fattr->ctime.useconds = ntohl(*p++);
|
if (fattr->type == NFCHR && fattr->rdev == NFS_FIFO_DEV) {
|
if (fattr->type == NFCHR && fattr->rdev == NFS_FIFO_DEV) {
|
fattr->type = NFFIFO;
|
fattr->type = NFFIFO;
|
fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
|
fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
|
fattr->rdev = 0;
|
fattr->rdev = 0;
|
}
|
}
|
return p;
|
return p;
|
}
|
}
|
|
|
static int *xdr_encode_sattr(int *p, struct nfs_sattr *sattr)
|
static int *xdr_encode_sattr(int *p, struct nfs_sattr *sattr)
|
{
|
{
|
*p++ = htonl(sattr->mode);
|
*p++ = htonl(sattr->mode);
|
*p++ = htonl(sattr->uid);
|
*p++ = htonl(sattr->uid);
|
*p++ = htonl(sattr->gid);
|
*p++ = htonl(sattr->gid);
|
*p++ = htonl(sattr->size);
|
*p++ = htonl(sattr->size);
|
*p++ = htonl(sattr->atime.seconds);
|
*p++ = htonl(sattr->atime.seconds);
|
*p++ = htonl(sattr->atime.useconds);
|
*p++ = htonl(sattr->atime.useconds);
|
*p++ = htonl(sattr->mtime.seconds);
|
*p++ = htonl(sattr->mtime.seconds);
|
*p++ = htonl(sattr->mtime.useconds);
|
*p++ = htonl(sattr->mtime.useconds);
|
return p;
|
return p;
|
}
|
}
|
|
|
static int *xdr_decode_entry(int *p, struct nfs_entry *entry)
|
static int *xdr_decode_entry(int *p, struct nfs_entry *entry)
|
{
|
{
|
entry->fileid = ntohl(*p++);
|
entry->fileid = ntohl(*p++);
|
if (!(p = xdr_decode_string(p, entry->name, NFS_MAXNAMLEN)))
|
if (!(p = xdr_decode_string(p, entry->name, NFS_MAXNAMLEN)))
|
return NULL;
|
return NULL;
|
entry->cookie = ntohl(*p++);
|
entry->cookie = ntohl(*p++);
|
entry->eof = 0;
|
entry->eof = 0;
|
return p;
|
return p;
|
}
|
}
|
|
|
static int *xdr_decode_fsinfo(int *p, struct nfs_fsinfo *res)
|
static int *xdr_decode_fsinfo(int *p, struct nfs_fsinfo *res)
|
{
|
{
|
res->tsize = ntohl(*p++);
|
res->tsize = ntohl(*p++);
|
res->bsize = ntohl(*p++);
|
res->bsize = ntohl(*p++);
|
res->blocks = ntohl(*p++);
|
res->blocks = ntohl(*p++);
|
res->bfree = ntohl(*p++);
|
res->bfree = ntohl(*p++);
|
res->bavail = ntohl(*p++);
|
res->bavail = ntohl(*p++);
|
return p;
|
return p;
|
}
|
}
|
|
|
/*
|
/*
|
* One function for each procedure in the NFS protocol.
|
* One function for each procedure in the NFS protocol.
|
*/
|
*/
|
|
|
int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
|
int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
|
struct nfs_fattr *fattr)
|
struct nfs_fattr *fattr)
|
{
|
{
|
int *p, *p0;
|
int *p, *p0;
|
int status;
|
int status;
|
int ruid = 0;
|
int ruid = 0;
|
|
|
PRINTK("NFS call getattr\n");
|
PRINTK("NFS call getattr\n");
|
if (!(p0 = nfs_rpc_alloc(server->rsize)))
|
if (!(p0 = nfs_rpc_alloc(server->rsize)))
|
return -EIO;
|
return -EIO;
|
retry:
|
retry:
|
p = nfs_rpc_header(p0, NFSPROC_GETATTR, ruid);
|
p = nfs_rpc_header(p0, NFSPROC_GETATTR, ruid);
|
p = xdr_encode_fhandle(p, fhandle);
|
p = xdr_encode_fhandle(p, fhandle);
|
if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
|
if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
if (!(p = nfs_rpc_verify(p0)))
|
if (!(p = nfs_rpc_verify(p0)))
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
p = xdr_decode_fattr(p, fattr);
|
p = xdr_decode_fattr(p, fattr);
|
PRINTK("NFS reply getattr\n");
|
PRINTK("NFS reply getattr\n");
|
/* status = 0; */
|
/* status = 0; */
|
}
|
}
|
else {
|
else {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
ruid = 1;
|
ruid = 1;
|
goto retry;
|
goto retry;
|
}
|
}
|
PRINTK("NFS reply getattr failed = %d\n", status);
|
PRINTK("NFS reply getattr failed = %d\n", status);
|
status = -nfs_stat_to_errno(status);
|
status = -nfs_stat_to_errno(status);
|
}
|
}
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
|
|
int nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle,
|
int nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle,
|
struct nfs_sattr *sattr, struct nfs_fattr *fattr)
|
struct nfs_sattr *sattr, struct nfs_fattr *fattr)
|
{
|
{
|
int *p, *p0;
|
int *p, *p0;
|
int status;
|
int status;
|
int ruid = 0;
|
int ruid = 0;
|
|
|
PRINTK("NFS call setattr\n");
|
PRINTK("NFS call setattr\n");
|
if (!(p0 = nfs_rpc_alloc(server->wsize)))
|
if (!(p0 = nfs_rpc_alloc(server->wsize)))
|
return -EIO;
|
return -EIO;
|
retry:
|
retry:
|
p = nfs_rpc_header(p0, NFSPROC_SETATTR, ruid);
|
p = nfs_rpc_header(p0, NFSPROC_SETATTR, ruid);
|
p = xdr_encode_fhandle(p, fhandle);
|
p = xdr_encode_fhandle(p, fhandle);
|
p = xdr_encode_sattr(p, sattr);
|
p = xdr_encode_sattr(p, sattr);
|
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
|
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
if (!(p = nfs_rpc_verify(p0)))
|
if (!(p = nfs_rpc_verify(p0)))
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
p = xdr_decode_fattr(p, fattr);
|
p = xdr_decode_fattr(p, fattr);
|
PRINTK("NFS reply setattr\n");
|
PRINTK("NFS reply setattr\n");
|
/* status = 0; */
|
/* status = 0; */
|
}
|
}
|
else {
|
else {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
ruid = 1;
|
ruid = 1;
|
goto retry;
|
goto retry;
|
}
|
}
|
PRINTK("NFS reply setattr failed = %d\n", status);
|
PRINTK("NFS reply setattr failed = %d\n", status);
|
status = -nfs_stat_to_errno(status);
|
status = -nfs_stat_to_errno(status);
|
}
|
}
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
|
|
int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, const char *name,
|
int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, const char *name,
|
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
{
|
{
|
int *p, *p0;
|
int *p, *p0;
|
int status;
|
int status;
|
int ruid = 0;
|
int ruid = 0;
|
|
|
PRINTK("NFS call lookup %s\n", name);
|
PRINTK("NFS call lookup %s\n", name);
|
#ifdef NFS_PROC_DEBUG
|
#ifdef NFS_PROC_DEBUG
|
if (!strcmp(name, "xyzzy"))
|
if (!strcmp(name, "xyzzy"))
|
proc_debug = 1 - proc_debug;
|
proc_debug = 1 - proc_debug;
|
#endif
|
#endif
|
if (!(p0 = nfs_rpc_alloc(server->rsize)))
|
if (!(p0 = nfs_rpc_alloc(server->rsize)))
|
return -EIO;
|
return -EIO;
|
retry:
|
retry:
|
p = nfs_rpc_header(p0, NFSPROC_LOOKUP, ruid);
|
p = nfs_rpc_header(p0, NFSPROC_LOOKUP, ruid);
|
p = xdr_encode_fhandle(p, dir);
|
p = xdr_encode_fhandle(p, dir);
|
p = xdr_encode_string(p, name);
|
p = xdr_encode_string(p, name);
|
if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
|
if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
if (!(p = nfs_rpc_verify(p0)))
|
if (!(p = nfs_rpc_verify(p0)))
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
p = xdr_decode_fhandle(p, fhandle);
|
p = xdr_decode_fhandle(p, fhandle);
|
p = xdr_decode_fattr(p, fattr);
|
p = xdr_decode_fattr(p, fattr);
|
PRINTK("NFS reply lookup\n");
|
PRINTK("NFS reply lookup\n");
|
/* status = 0; */
|
/* status = 0; */
|
}
|
}
|
else {
|
else {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
ruid = 1;
|
ruid = 1;
|
goto retry;
|
goto retry;
|
}
|
}
|
PRINTK("NFS reply lookup failed = %d\n", status);
|
PRINTK("NFS reply lookup failed = %d\n", status);
|
status = -nfs_stat_to_errno(status);
|
status = -nfs_stat_to_errno(status);
|
}
|
}
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
|
|
int nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle,
|
int nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle,
|
int **p0, char **string, unsigned int *len, unsigned int maxlen)
|
int **p0, char **string, unsigned int *len, unsigned int maxlen)
|
{
|
{
|
int *p;
|
int *p;
|
int status, ruid = 0;
|
int status, ruid = 0;
|
|
|
PRINTK("NFS call readlink\n");
|
PRINTK("NFS call readlink\n");
|
if (!(*p0 = nfs_rpc_alloc(server->rsize)))
|
if (!(*p0 = nfs_rpc_alloc(server->rsize)))
|
return -EIO;
|
return -EIO;
|
retry:
|
retry:
|
p = nfs_rpc_header(*p0, NFSPROC_READLINK, ruid);
|
p = nfs_rpc_header(*p0, NFSPROC_READLINK, ruid);
|
p = xdr_encode_fhandle(p, fhandle);
|
p = xdr_encode_fhandle(p, fhandle);
|
if ((status = nfs_rpc_call(server, *p0, p, server->rsize)) < 0)
|
if ((status = nfs_rpc_call(server, *p0, p, server->rsize)) < 0)
|
return status;
|
return status;
|
if (!(p = nfs_rpc_verify(*p0)))
|
if (!(p = nfs_rpc_verify(*p0)))
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
if (!(p = xdr_decode_string2(p, string, len, maxlen))) {
|
if (!(p = xdr_decode_string2(p, string, len, maxlen))) {
|
printk("nfs_proc_readlink: giant pathname\n");
|
printk("nfs_proc_readlink: giant pathname\n");
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
}
|
}
|
else /* status = 0, */
|
else /* status = 0, */
|
PRINTK("NFS reply readlink\n");
|
PRINTK("NFS reply readlink\n");
|
}
|
}
|
else {
|
else {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
ruid = 1;
|
ruid = 1;
|
goto retry;
|
goto retry;
|
}
|
}
|
PRINTK("NFS reply readlink failed = %d\n", status);
|
PRINTK("NFS reply readlink failed = %d\n", status);
|
status = -nfs_stat_to_errno(status);
|
status = -nfs_stat_to_errno(status);
|
}
|
}
|
return status;
|
return status;
|
}
|
}
|
|
|
int nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle,
|
int nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle,
|
int offset, int count, char *data, struct nfs_fattr *fattr)
|
int offset, int count, char *data, struct nfs_fattr *fattr)
|
{
|
{
|
int *p, *p0;
|
int *p, *p0;
|
int status;
|
int status;
|
int ruid = 0;
|
int ruid = 0;
|
int len;
|
int len;
|
|
|
PRINTK("NFS call read %d @ %d\n", count, offset);
|
PRINTK("NFS call read %d @ %d\n", count, offset);
|
if (!(p0 = nfs_rpc_alloc(server->rsize)))
|
if (!(p0 = nfs_rpc_alloc(server->rsize)))
|
return -EIO;
|
return -EIO;
|
retry:
|
retry:
|
p = nfs_rpc_header(p0, NFSPROC_READ, ruid);
|
p = nfs_rpc_header(p0, NFSPROC_READ, ruid);
|
p = xdr_encode_fhandle(p, fhandle);
|
p = xdr_encode_fhandle(p, fhandle);
|
*p++ = htonl(offset);
|
*p++ = htonl(offset);
|
*p++ = htonl(count);
|
*p++ = htonl(count);
|
*p++ = htonl(count); /* traditional, could be any value */
|
*p++ = htonl(count); /* traditional, could be any value */
|
if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
|
if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
if (!(p = nfs_rpc_verify(p0)))
|
if (!(p = nfs_rpc_verify(p0)))
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
p = xdr_decode_fattr(p, fattr);
|
p = xdr_decode_fattr(p, fattr);
|
if (!(p = xdr_decode_data(p, data, &len, count))) {
|
if (!(p = xdr_decode_data(p, data, &len, count))) {
|
printk("nfs_proc_read: giant data size\n");
|
printk("nfs_proc_read: giant data size\n");
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
}
|
}
|
else {
|
else {
|
status = len;
|
status = len;
|
PRINTK("NFS reply read %d\n", len);
|
PRINTK("NFS reply read %d\n", len);
|
}
|
}
|
}
|
}
|
else {
|
else {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
ruid = 1;
|
ruid = 1;
|
goto retry;
|
goto retry;
|
}
|
}
|
PRINTK("NFS reply read failed = %d\n", status);
|
PRINTK("NFS reply read failed = %d\n", status);
|
status = -nfs_stat_to_errno(status);
|
status = -nfs_stat_to_errno(status);
|
}
|
}
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
|
|
int
|
int
|
nfs_proc_read_request(struct rpc_ioreq *req, struct nfs_server *server,
|
nfs_proc_read_request(struct rpc_ioreq *req, struct nfs_server *server,
|
struct nfs_fh *fh, unsigned long offset,
|
struct nfs_fh *fh, unsigned long offset,
|
unsigned long count, __u32 *buf)
|
unsigned long count, __u32 *buf)
|
{
|
{
|
__u32 *p, *p0;
|
__u32 *p, *p0;
|
int len;
|
int len;
|
|
|
PRINTK("NFS reqst read %ld @ %ld\n", count, offset);
|
PRINTK("NFS reqst read %ld @ %ld\n", count, offset);
|
if (!(p0 = nfs_rpc_alloc(NFS_SLACK_SPACE)))
|
if (!(p0 = nfs_rpc_alloc(NFS_SLACK_SPACE)))
|
return -EIO;
|
return -EIO;
|
|
|
p = nfs_rpc_header(p0, NFSPROC_READ, 0);
|
p = nfs_rpc_header(p0, NFSPROC_READ, 0);
|
p = xdr_encode_fhandle(p, fh);
|
p = xdr_encode_fhandle(p, fh);
|
*p++ = htonl(offset);
|
*p++ = htonl(offset);
|
*p++ = htonl(count);
|
*p++ = htonl(count);
|
*p++ = htonl(count); /* traditional, could be any value */
|
*p++ = htonl(count); /* traditional, could be any value */
|
req->rq_svec[0].iov_base = p0;
|
req->rq_svec[0].iov_base = p0;
|
req->rq_svec[0].iov_len = (p - p0) << 2;
|
req->rq_svec[0].iov_len = (p - p0) << 2;
|
req->rq_slen = (p - p0) << 2;
|
req->rq_slen = (p - p0) << 2;
|
req->rq_snr = 1;
|
req->rq_snr = 1;
|
|
|
len = (6 + 1 + 17 + 1); /* standard READ reply header */
|
len = (6 + 1 + 17 + 1); /* standard READ reply header */
|
req->rq_rvec[0].iov_base = p0;
|
req->rq_rvec[0].iov_base = p0;
|
req->rq_rvec[0].iov_len = len << 2;
|
req->rq_rvec[0].iov_len = len << 2;
|
req->rq_rvec[1].iov_base = buf;
|
req->rq_rvec[1].iov_base = buf;
|
req->rq_rvec[1].iov_len = count;
|
req->rq_rvec[1].iov_len = count;
|
req->rq_rvec[2].iov_base = p0 + len; /* spill buffer */
|
req->rq_rvec[2].iov_base = p0 + len; /* spill buffer */
|
req->rq_rvec[2].iov_len = (NFS_SLACK_SPACE - len) << 2;
|
req->rq_rvec[2].iov_len = (NFS_SLACK_SPACE - len) << 2;
|
req->rq_rlen = count + NFS_SLACK_SPACE;
|
req->rq_rlen = count + NFS_SLACK_SPACE;
|
req->rq_rnr = 3;
|
req->rq_rnr = 3;
|
|
|
req->rq_addr = &server->toaddr;
|
req->rq_addr = &server->toaddr;
|
req->rq_alen = sizeof(server->toaddr);
|
req->rq_alen = sizeof(server->toaddr);
|
|
|
return rpc_transmit(server->rsock, req);
|
return rpc_transmit(server->rsock, req);
|
}
|
}
|
|
|
int
|
int
|
nfs_proc_read_reply(struct rpc_ioreq *req, struct nfs_fattr *fattr)
|
nfs_proc_read_reply(struct rpc_ioreq *req, struct nfs_fattr *fattr)
|
{
|
{
|
int status;
|
int status;
|
__u32 *p0, *p;
|
__u32 *p0, *p;
|
int count;
|
int count;
|
|
|
p0 = (__u32 *) req->rq_rvec[0].iov_base;
|
p0 = (__u32 *) req->rq_rvec[0].iov_base;
|
|
|
if (!(p = nfs_rpc_verify(p0))) {
|
if (!(p = nfs_rpc_verify(p0))) {
|
/* Tell the upper layers to retry */
|
/* Tell the upper layers to retry */
|
status = -EAGAIN;
|
status = -EAGAIN;
|
/* status = -errno_NFSERR_IO; */
|
/* status = -errno_NFSERR_IO; */
|
} else if ((status = ntohl(*p++)) == NFS_OK) {
|
} else if ((status = ntohl(*p++)) == NFS_OK) {
|
p = xdr_decode_fattr(p, fattr);
|
p = xdr_decode_fattr(p, fattr);
|
count = ntohl(*p++);
|
count = ntohl(*p++);
|
if (p != req->rq_rvec[2].iov_base) {
|
if (p != req->rq_rvec[2].iov_base) {
|
/* unexpected RPC reply header size. punt.
|
/* unexpected RPC reply header size. punt.
|
* fixme: move iovec contents to align data
|
* fixme: move iovec contents to align data
|
* on page boundary and adjust RPC header size
|
* on page boundary and adjust RPC header size
|
* guess. */
|
* guess. */
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
PRINTK("NFS reply read odd header size %d\n",
|
PRINTK("NFS reply read odd header size %d\n",
|
(p - p0) << 2);
|
(p - p0) << 2);
|
} else {
|
} else {
|
status = count;
|
status = count;
|
PRINTK("NFS reply read %d\n", count);
|
PRINTK("NFS reply read %d\n", count);
|
}
|
}
|
}
|
}
|
else {
|
else {
|
PRINTK("NFS reply read failed = %d\n", status);
|
PRINTK("NFS reply read failed = %d\n", status);
|
status = -nfs_stat_to_errno(status);
|
status = -nfs_stat_to_errno(status);
|
}
|
}
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
|
|
int nfs_proc_write(struct inode * inode, int offset,
|
int nfs_proc_write(struct inode * inode, int offset,
|
int count, const char *data, struct nfs_fattr *fattr)
|
int count, const char *data, struct nfs_fattr *fattr)
|
{
|
{
|
int *p, *p0;
|
int *p, *p0;
|
int status;
|
int status;
|
int ruid = 0;
|
int ruid = 0;
|
void * kdata; /* address of kernel copy */
|
void * kdata; /* address of kernel copy */
|
struct nfs_server * server = NFS_SERVER(inode);
|
struct nfs_server * server = NFS_SERVER(inode);
|
struct nfs_fh *fhandle = NFS_FH(inode);
|
struct nfs_fh *fhandle = NFS_FH(inode);
|
|
|
PRINTK("NFS call write %d @ %d\n", count, offset);
|
PRINTK("NFS call write %d @ %d\n", count, offset);
|
if (!(p0 = nfs_rpc_alloc(server->wsize)))
|
if (!(p0 = nfs_rpc_alloc(server->wsize)))
|
return -EIO;
|
return -EIO;
|
retry:
|
retry:
|
p = nfs_rpc_header(p0, NFSPROC_WRITE, ruid);
|
p = nfs_rpc_header(p0, NFSPROC_WRITE, ruid);
|
p = xdr_encode_fhandle(p, fhandle);
|
p = xdr_encode_fhandle(p, fhandle);
|
*p++ = htonl(offset); /* traditional, could be any value */
|
*p++ = htonl(offset); /* traditional, could be any value */
|
*p++ = htonl(offset);
|
*p++ = htonl(offset);
|
*p++ = htonl(count); /* traditional, could be any value */
|
*p++ = htonl(count); /* traditional, could be any value */
|
kdata = (void *) (p+1); /* start of data in RPC buffer */
|
kdata = (void *) (p+1); /* start of data in RPC buffer */
|
p = xdr_encode_data(p, data, count);
|
p = xdr_encode_data(p, data, count);
|
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
|
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
if (!(p = nfs_rpc_verify(p0)))
|
if (!(p = nfs_rpc_verify(p0)))
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
update_vm_cache(inode, offset, kdata, count);
|
update_vm_cache(inode, offset, kdata, count);
|
p = xdr_decode_fattr(p, fattr);
|
p = xdr_decode_fattr(p, fattr);
|
PRINTK("NFS reply write\n");
|
PRINTK("NFS reply write\n");
|
/* status = 0; */
|
/* status = 0; */
|
}
|
}
|
else {
|
else {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
ruid = 1;
|
ruid = 1;
|
goto retry;
|
goto retry;
|
}
|
}
|
PRINTK("NFS reply write failed = %d\n", status);
|
PRINTK("NFS reply write failed = %d\n", status);
|
status = -nfs_stat_to_errno(status);
|
status = -nfs_stat_to_errno(status);
|
}
|
}
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
|
|
int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
|
int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
|
const char *name, struct nfs_sattr *sattr,
|
const char *name, struct nfs_sattr *sattr,
|
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
{
|
{
|
int *p, *p0;
|
int *p, *p0;
|
int status;
|
int status;
|
int ruid = 0;
|
int ruid = 0;
|
|
|
PRINTK("NFS call create %s\n", name);
|
PRINTK("NFS call create %s\n", name);
|
if (!(p0 = nfs_rpc_alloc(server->wsize)))
|
if (!(p0 = nfs_rpc_alloc(server->wsize)))
|
return -EIO;
|
return -EIO;
|
retry:
|
retry:
|
p = nfs_rpc_header(p0, NFSPROC_CREATE, ruid);
|
p = nfs_rpc_header(p0, NFSPROC_CREATE, ruid);
|
p = xdr_encode_fhandle(p, dir);
|
p = xdr_encode_fhandle(p, dir);
|
p = xdr_encode_string(p, name);
|
p = xdr_encode_string(p, name);
|
p = xdr_encode_sattr(p, sattr);
|
p = xdr_encode_sattr(p, sattr);
|
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
|
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
if (!(p = nfs_rpc_verify(p0)))
|
if (!(p = nfs_rpc_verify(p0)))
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
p = xdr_decode_fhandle(p, fhandle);
|
p = xdr_decode_fhandle(p, fhandle);
|
p = xdr_decode_fattr(p, fattr);
|
p = xdr_decode_fattr(p, fattr);
|
PRINTK("NFS reply create\n");
|
PRINTK("NFS reply create\n");
|
/* status = 0; */
|
/* status = 0; */
|
}
|
}
|
else {
|
else {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
ruid = 1;
|
ruid = 1;
|
goto retry;
|
goto retry;
|
}
|
}
|
PRINTK("NFS reply create failed = %d\n", status);
|
PRINTK("NFS reply create failed = %d\n", status);
|
status = -nfs_stat_to_errno(status);
|
status = -nfs_stat_to_errno(status);
|
}
|
}
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
|
|
int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir, const char *name)
|
int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir, const char *name)
|
{
|
{
|
int *p, *p0;
|
int *p, *p0;
|
int status;
|
int status;
|
int ruid = 0;
|
int ruid = 0;
|
|
|
PRINTK("NFS call remove %s\n", name);
|
PRINTK("NFS call remove %s\n", name);
|
if (!(p0 = nfs_rpc_alloc(server->wsize)))
|
if (!(p0 = nfs_rpc_alloc(server->wsize)))
|
return -EIO;
|
return -EIO;
|
retry:
|
retry:
|
p = nfs_rpc_header(p0, NFSPROC_REMOVE, ruid);
|
p = nfs_rpc_header(p0, NFSPROC_REMOVE, ruid);
|
p = xdr_encode_fhandle(p, dir);
|
p = xdr_encode_fhandle(p, dir);
|
p = xdr_encode_string(p, name);
|
p = xdr_encode_string(p, name);
|
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
|
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
if (!(p = nfs_rpc_verify(p0)))
|
if (!(p = nfs_rpc_verify(p0)))
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
PRINTK("NFS reply remove\n");
|
PRINTK("NFS reply remove\n");
|
/* status = 0; */
|
/* status = 0; */
|
}
|
}
|
else {
|
else {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
ruid = 1;
|
ruid = 1;
|
goto retry;
|
goto retry;
|
}
|
}
|
PRINTK("NFS reply remove failed = %d\n", status);
|
PRINTK("NFS reply remove failed = %d\n", status);
|
status = -nfs_stat_to_errno(status);
|
status = -nfs_stat_to_errno(status);
|
}
|
}
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
|
|
int nfs_proc_rename(struct nfs_server *server,
|
int nfs_proc_rename(struct nfs_server *server,
|
struct nfs_fh *old_dir, const char *old_name,
|
struct nfs_fh *old_dir, const char *old_name,
|
struct nfs_fh *new_dir, const char *new_name,
|
struct nfs_fh *new_dir, const char *new_name,
|
int must_be_dir)
|
int must_be_dir)
|
{
|
{
|
int *p, *p0;
|
int *p, *p0;
|
int status;
|
int status;
|
int ruid = 0;
|
int ruid = 0;
|
|
|
/*
|
/*
|
* Disallow "rename()" with trailing slashes over NFS: getting
|
* Disallow "rename()" with trailing slashes over NFS: getting
|
* POSIX.1 behaviour is just too unlikely.
|
* POSIX.1 behaviour is just too unlikely.
|
*/
|
*/
|
if (must_be_dir)
|
if (must_be_dir)
|
return -EINVAL;
|
return -EINVAL;
|
PRINTK("NFS call rename %s -> %s\n", old_name, new_name);
|
PRINTK("NFS call rename %s -> %s\n", old_name, new_name);
|
if (!(p0 = nfs_rpc_alloc(server->wsize)))
|
if (!(p0 = nfs_rpc_alloc(server->wsize)))
|
return -EIO;
|
return -EIO;
|
retry:
|
retry:
|
p = nfs_rpc_header(p0, NFSPROC_RENAME, ruid);
|
p = nfs_rpc_header(p0, NFSPROC_RENAME, ruid);
|
p = xdr_encode_fhandle(p, old_dir);
|
p = xdr_encode_fhandle(p, old_dir);
|
p = xdr_encode_string(p, old_name);
|
p = xdr_encode_string(p, old_name);
|
p = xdr_encode_fhandle(p, new_dir);
|
p = xdr_encode_fhandle(p, new_dir);
|
p = xdr_encode_string(p, new_name);
|
p = xdr_encode_string(p, new_name);
|
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
|
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
if (!(p = nfs_rpc_verify(p0)))
|
if (!(p = nfs_rpc_verify(p0)))
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
PRINTK("NFS reply rename\n");
|
PRINTK("NFS reply rename\n");
|
/* status = 0; */
|
/* status = 0; */
|
}
|
}
|
else {
|
else {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
ruid = 1;
|
ruid = 1;
|
goto retry;
|
goto retry;
|
}
|
}
|
PRINTK("NFS reply rename failed = %d\n", status);
|
PRINTK("NFS reply rename failed = %d\n", status);
|
status = -nfs_stat_to_errno(status);
|
status = -nfs_stat_to_errno(status);
|
}
|
}
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
|
|
int nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
|
int nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
|
struct nfs_fh *dir, const char *name)
|
struct nfs_fh *dir, const char *name)
|
{
|
{
|
int *p, *p0;
|
int *p, *p0;
|
int status;
|
int status;
|
int ruid = 0;
|
int ruid = 0;
|
|
|
PRINTK("NFS call link %s\n", name);
|
PRINTK("NFS call link %s\n", name);
|
if (!(p0 = nfs_rpc_alloc(server->wsize)))
|
if (!(p0 = nfs_rpc_alloc(server->wsize)))
|
return -EIO;
|
return -EIO;
|
retry:
|
retry:
|
p = nfs_rpc_header(p0, NFSPROC_LINK, ruid);
|
p = nfs_rpc_header(p0, NFSPROC_LINK, ruid);
|
p = xdr_encode_fhandle(p, fhandle);
|
p = xdr_encode_fhandle(p, fhandle);
|
p = xdr_encode_fhandle(p, dir);
|
p = xdr_encode_fhandle(p, dir);
|
p = xdr_encode_string(p, name);
|
p = xdr_encode_string(p, name);
|
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
|
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
if (!(p = nfs_rpc_verify(p0)))
|
if (!(p = nfs_rpc_verify(p0)))
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
PRINTK("NFS reply link\n");
|
PRINTK("NFS reply link\n");
|
/* status = 0; */
|
/* status = 0; */
|
}
|
}
|
else {
|
else {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
ruid = 1;
|
ruid = 1;
|
goto retry;
|
goto retry;
|
}
|
}
|
PRINTK("NFS reply link failed = %d\n", status);
|
PRINTK("NFS reply link failed = %d\n", status);
|
status = -nfs_stat_to_errno(status);
|
status = -nfs_stat_to_errno(status);
|
}
|
}
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
|
|
int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
|
int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
|
const char *name, const char *path, struct nfs_sattr *sattr)
|
const char *name, const char *path, struct nfs_sattr *sattr)
|
{
|
{
|
int *p, *p0;
|
int *p, *p0;
|
int status;
|
int status;
|
int ruid = 0;
|
int ruid = 0;
|
|
|
PRINTK("NFS call symlink %s -> %s\n", name, path);
|
PRINTK("NFS call symlink %s -> %s\n", name, path);
|
if (!(p0 = nfs_rpc_alloc(server->wsize)))
|
if (!(p0 = nfs_rpc_alloc(server->wsize)))
|
return -EIO;
|
return -EIO;
|
retry:
|
retry:
|
p = nfs_rpc_header(p0, NFSPROC_SYMLINK, ruid);
|
p = nfs_rpc_header(p0, NFSPROC_SYMLINK, ruid);
|
p = xdr_encode_fhandle(p, dir);
|
p = xdr_encode_fhandle(p, dir);
|
p = xdr_encode_string(p, name);
|
p = xdr_encode_string(p, name);
|
p = xdr_encode_string(p, path);
|
p = xdr_encode_string(p, path);
|
p = xdr_encode_sattr(p, sattr);
|
p = xdr_encode_sattr(p, sattr);
|
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
|
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
if (!(p = nfs_rpc_verify(p0)))
|
if (!(p = nfs_rpc_verify(p0)))
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
PRINTK("NFS reply symlink\n");
|
PRINTK("NFS reply symlink\n");
|
/* status = 0; */
|
/* status = 0; */
|
}
|
}
|
else {
|
else {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
ruid = 1;
|
ruid = 1;
|
goto retry;
|
goto retry;
|
}
|
}
|
PRINTK("NFS reply symlink failed = %d\n", status);
|
PRINTK("NFS reply symlink failed = %d\n", status);
|
status = -nfs_stat_to_errno(status);
|
status = -nfs_stat_to_errno(status);
|
}
|
}
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
|
|
int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
|
int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
|
const char *name, struct nfs_sattr *sattr,
|
const char *name, struct nfs_sattr *sattr,
|
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
|
{
|
{
|
int *p, *p0;
|
int *p, *p0;
|
int status;
|
int status;
|
int ruid = 0;
|
int ruid = 0;
|
|
|
PRINTK("NFS call mkdir %s\n", name);
|
PRINTK("NFS call mkdir %s\n", name);
|
if (!(p0 = nfs_rpc_alloc(server->wsize)))
|
if (!(p0 = nfs_rpc_alloc(server->wsize)))
|
return -EIO;
|
return -EIO;
|
retry:
|
retry:
|
p = nfs_rpc_header(p0, NFSPROC_MKDIR, ruid);
|
p = nfs_rpc_header(p0, NFSPROC_MKDIR, ruid);
|
p = xdr_encode_fhandle(p, dir);
|
p = xdr_encode_fhandle(p, dir);
|
p = xdr_encode_string(p, name);
|
p = xdr_encode_string(p, name);
|
p = xdr_encode_sattr(p, sattr);
|
p = xdr_encode_sattr(p, sattr);
|
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
|
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
if (!(p = nfs_rpc_verify(p0)))
|
if (!(p = nfs_rpc_verify(p0)))
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
p = xdr_decode_fhandle(p, fhandle);
|
p = xdr_decode_fhandle(p, fhandle);
|
p = xdr_decode_fattr(p, fattr);
|
p = xdr_decode_fattr(p, fattr);
|
PRINTK("NFS reply mkdir\n");
|
PRINTK("NFS reply mkdir\n");
|
/* status = 0; */
|
/* status = 0; */
|
}
|
}
|
else {
|
else {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
ruid = 1;
|
ruid = 1;
|
goto retry;
|
goto retry;
|
}
|
}
|
PRINTK("NFS reply mkdir failed = %d\n", status);
|
PRINTK("NFS reply mkdir failed = %d\n", status);
|
status = -nfs_stat_to_errno(status);
|
status = -nfs_stat_to_errno(status);
|
}
|
}
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
|
|
int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name)
|
int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name)
|
{
|
{
|
int *p, *p0;
|
int *p, *p0;
|
int status;
|
int status;
|
int ruid = 0;
|
int ruid = 0;
|
|
|
PRINTK("NFS call rmdir %s\n", name);
|
PRINTK("NFS call rmdir %s\n", name);
|
if (!(p0 = nfs_rpc_alloc(server->wsize)))
|
if (!(p0 = nfs_rpc_alloc(server->wsize)))
|
return -EIO;
|
return -EIO;
|
retry:
|
retry:
|
p = nfs_rpc_header(p0, NFSPROC_RMDIR, ruid);
|
p = nfs_rpc_header(p0, NFSPROC_RMDIR, ruid);
|
p = xdr_encode_fhandle(p, dir);
|
p = xdr_encode_fhandle(p, dir);
|
p = xdr_encode_string(p, name);
|
p = xdr_encode_string(p, name);
|
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
|
if ((status = nfs_rpc_call(server, p0, p, server->wsize)) < 0) {
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
if (!(p = nfs_rpc_verify(p0)))
|
if (!(p = nfs_rpc_verify(p0)))
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
PRINTK("NFS reply rmdir\n");
|
PRINTK("NFS reply rmdir\n");
|
/* status = 0; */
|
/* status = 0; */
|
}
|
}
|
else {
|
else {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
ruid = 1;
|
ruid = 1;
|
goto retry;
|
goto retry;
|
}
|
}
|
PRINTK("NFS reply rmdir failed = %d\n", status);
|
PRINTK("NFS reply rmdir failed = %d\n", status);
|
status = -nfs_stat_to_errno(status);
|
status = -nfs_stat_to_errno(status);
|
}
|
}
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
|
|
int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
|
int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
|
int cookie, int count, struct nfs_entry *entry)
|
int cookie, int count, struct nfs_entry *entry)
|
{
|
{
|
int *p, *p0;
|
int *p, *p0;
|
int status;
|
int status;
|
int ruid = 0;
|
int ruid = 0;
|
int i;
|
int i;
|
int size;
|
int size;
|
int eof;
|
int eof;
|
|
|
PRINTK("NFS call readdir %d @ %d\n", count, cookie);
|
PRINTK("NFS call readdir %d @ %d\n", count, cookie);
|
size = server->rsize;
|
size = server->rsize;
|
if (!(p0 = nfs_rpc_alloc(server->rsize)))
|
if (!(p0 = nfs_rpc_alloc(server->rsize)))
|
return -EIO;
|
return -EIO;
|
retry:
|
retry:
|
p = nfs_rpc_header(p0, NFSPROC_READDIR, ruid);
|
p = nfs_rpc_header(p0, NFSPROC_READDIR, ruid);
|
p = xdr_encode_fhandle(p, fhandle);
|
p = xdr_encode_fhandle(p, fhandle);
|
*p++ = htonl(cookie);
|
*p++ = htonl(cookie);
|
*p++ = htonl(size);
|
*p++ = htonl(size);
|
if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
|
if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
if (!(p = nfs_rpc_verify(p0)))
|
if (!(p = nfs_rpc_verify(p0)))
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
for (i = 0; i < count && *p++; i++) {
|
for (i = 0; i < count && *p++; i++) {
|
if (!(p = xdr_decode_entry(p, entry++)))
|
if (!(p = xdr_decode_entry(p, entry++)))
|
break;
|
break;
|
}
|
}
|
if (!p) {
|
if (!p) {
|
printk("nfs_proc_readdir: giant filename\n");
|
printk("nfs_proc_readdir: giant filename\n");
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
}
|
}
|
else {
|
else {
|
eof = (i == count && !*p++ && *p++)
|
eof = (i == count && !*p++ && *p++)
|
|| (i < count && *p++);
|
|| (i < count && *p++);
|
if (eof && i)
|
if (eof && i)
|
entry[-1].eof = 1;
|
entry[-1].eof = 1;
|
PRINTK("NFS reply readdir %d %s\n", i,
|
PRINTK("NFS reply readdir %d %s\n", i,
|
eof ? "eof" : "");
|
eof ? "eof" : "");
|
status = i;
|
status = i;
|
}
|
}
|
}
|
}
|
else {
|
else {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
ruid = 1;
|
ruid = 1;
|
goto retry;
|
goto retry;
|
}
|
}
|
PRINTK("NFS reply readdir failed = %d\n", status);
|
PRINTK("NFS reply readdir failed = %d\n", status);
|
status = -nfs_stat_to_errno(status);
|
status = -nfs_stat_to_errno(status);
|
}
|
}
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
|
|
int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
|
int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
|
struct nfs_fsinfo *res)
|
struct nfs_fsinfo *res)
|
{
|
{
|
int *p, *p0;
|
int *p, *p0;
|
int status;
|
int status;
|
int ruid = 0;
|
int ruid = 0;
|
|
|
PRINTK("NFS call statfs\n");
|
PRINTK("NFS call statfs\n");
|
if (!(p0 = nfs_rpc_alloc(server->rsize)))
|
if (!(p0 = nfs_rpc_alloc(server->rsize)))
|
return -EIO;
|
return -EIO;
|
retry:
|
retry:
|
p = nfs_rpc_header(p0, NFSPROC_STATFS, ruid);
|
p = nfs_rpc_header(p0, NFSPROC_STATFS, ruid);
|
p = xdr_encode_fhandle(p, fhandle);
|
p = xdr_encode_fhandle(p, fhandle);
|
if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
|
if ((status = nfs_rpc_call(server, p0, p, server->rsize)) < 0) {
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
if (!(p = nfs_rpc_verify(p0)))
|
if (!(p = nfs_rpc_verify(p0)))
|
status = -errno_NFSERR_IO;
|
status = -errno_NFSERR_IO;
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
else if ((status = ntohl(*p++)) == NFS_OK) {
|
p = xdr_decode_fsinfo(p, res);
|
p = xdr_decode_fsinfo(p, res);
|
PRINTK("NFS reply statfs\n");
|
PRINTK("NFS reply statfs\n");
|
/* status = 0; */
|
/* status = 0; */
|
}
|
}
|
else {
|
else {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
if (!ruid && current->fsuid == 0 && current->uid != 0) {
|
ruid = 1;
|
ruid = 1;
|
goto retry;
|
goto retry;
|
}
|
}
|
PRINTK("NFS reply statfs failed = %d\n", status);
|
PRINTK("NFS reply statfs failed = %d\n", status);
|
status = -nfs_stat_to_errno(status);
|
status = -nfs_stat_to_errno(status);
|
}
|
}
|
nfs_rpc_free(p0);
|
nfs_rpc_free(p0);
|
return status;
|
return status;
|
}
|
}
|
|
|
/*
|
/*
|
* Here are a few RPC-assist functions.
|
* Here are a few RPC-assist functions.
|
*/
|
*/
|
|
|
int *rpc_header(int *p, int procedure, int program, int version,
|
int *rpc_header(int *p, int procedure, int program, int version,
|
int uid, int gid, int *groups)
|
int uid, int gid, int *groups)
|
{
|
{
|
int *p1, *p2;
|
int *p1, *p2;
|
int i;
|
int i;
|
static int xid = 0;
|
static int xid = 0;
|
unsigned char *sys = (unsigned char *) system_utsname.nodename;
|
unsigned char *sys = (unsigned char *) system_utsname.nodename;
|
|
|
if (xid == 0) {
|
if (xid == 0) {
|
xid = CURRENT_TIME;
|
xid = CURRENT_TIME;
|
xid ^= (sys[3]<<24) | (sys[2]<<16) | (sys[1]<<8) | sys[0];
|
xid ^= (sys[3]<<24) | (sys[2]<<16) | (sys[1]<<8) | sys[0];
|
}
|
}
|
*p++ = htonl(++xid);
|
*p++ = htonl(++xid);
|
*p++ = htonl(RPC_CALL);
|
*p++ = htonl(RPC_CALL);
|
*p++ = htonl(RPC_VERSION);
|
*p++ = htonl(RPC_VERSION);
|
*p++ = htonl(program);
|
*p++ = htonl(program);
|
*p++ = htonl(version);
|
*p++ = htonl(version);
|
*p++ = htonl(procedure);
|
*p++ = htonl(procedure);
|
*p++ = htonl(RPC_AUTH_UNIX);
|
*p++ = htonl(RPC_AUTH_UNIX);
|
p1 = p++;
|
p1 = p++;
|
*p++ = htonl(CURRENT_TIME); /* traditional, could be anything */
|
*p++ = htonl(CURRENT_TIME); /* traditional, could be anything */
|
p = xdr_encode_string(p, (char *) sys);
|
p = xdr_encode_string(p, (char *) sys);
|
*p++ = htonl(uid);
|
*p++ = htonl(uid);
|
*p++ = htonl(gid);
|
*p++ = htonl(gid);
|
p2 = p++;
|
p2 = p++;
|
for (i = 0; i < 16 && i < NGROUPS && groups[i] != NOGROUP; i++)
|
for (i = 0; i < 16 && i < NGROUPS && groups[i] != NOGROUP; i++)
|
*p++ = htonl(groups[i]);
|
*p++ = htonl(groups[i]);
|
*p2 = htonl(i);
|
*p2 = htonl(i);
|
*p1 = htonl((p - (p1 + 1)) << 2);
|
*p1 = htonl((p - (p1 + 1)) << 2);
|
*p++ = htonl(RPC_AUTH_NULL);
|
*p++ = htonl(RPC_AUTH_NULL);
|
*p++ = htonl(0);
|
*p++ = htonl(0);
|
return p;
|
return p;
|
}
|
}
|
|
|
|
|
static int *nfs_rpc_header(int *p, int procedure, int ruid)
|
static int *nfs_rpc_header(int *p, int procedure, int ruid)
|
{
|
{
|
return rpc_header(p, procedure, NFS_PROGRAM, NFS_VERSION,
|
return rpc_header(p, procedure, NFS_PROGRAM, NFS_VERSION,
|
(ruid ? current->uid : current->fsuid),
|
(ruid ? current->uid : current->fsuid),
|
current->egid, current->groups);
|
current->egid, current->groups);
|
}
|
}
|
|
|
|
|
int *rpc_verify(int *p)
|
int *rpc_verify(int *p)
|
{
|
{
|
unsigned int n;
|
unsigned int n;
|
|
|
p++;
|
p++;
|
if ((n = ntohl(*p++)) != RPC_REPLY) {
|
if ((n = ntohl(*p++)) != RPC_REPLY) {
|
printk("nfs_rpc_verify: not an RPC reply: %x\n", n);
|
printk("nfs_rpc_verify: not an RPC reply: %x\n", n);
|
return NULL;
|
return NULL;
|
}
|
}
|
if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
|
if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
|
printk("nfs_rpc_verify: RPC call rejected: %d\n", n);
|
printk("nfs_rpc_verify: RPC call rejected: %d\n", n);
|
return NULL;
|
return NULL;
|
}
|
}
|
switch (n = ntohl(*p++)) {
|
switch (n = ntohl(*p++)) {
|
case RPC_AUTH_NULL: case RPC_AUTH_UNIX: case RPC_AUTH_SHORT:
|
case RPC_AUTH_NULL: case RPC_AUTH_UNIX: case RPC_AUTH_SHORT:
|
break;
|
break;
|
default:
|
default:
|
printk("nfs_rpc_verify: bad RPC authentication type: %d\n", n);
|
printk("nfs_rpc_verify: bad RPC authentication type: %d\n", n);
|
return NULL;
|
return NULL;
|
}
|
}
|
if ((n = ntohl(*p++)) > 400) {
|
if ((n = ntohl(*p++)) > 400) {
|
printk("nfs_rpc_verify: giant auth size\n");
|
printk("nfs_rpc_verify: giant auth size\n");
|
return NULL;
|
return NULL;
|
}
|
}
|
p += QUADLEN(n);
|
p += QUADLEN(n);
|
if ((n = ntohl(*p++)) != RPC_SUCCESS) {
|
if ((n = ntohl(*p++)) != RPC_SUCCESS) {
|
printk("nfs_rpc_verify: RPC call failed: %d\n", n);
|
printk("nfs_rpc_verify: RPC call failed: %d\n", n);
|
return NULL;
|
return NULL;
|
}
|
}
|
return p;
|
return p;
|
}
|
}
|
|
|
|
|
static int *nfs_rpc_verify(int *p)
|
static int *nfs_rpc_verify(int *p)
|
{
|
{
|
return rpc_verify(p);
|
return rpc_verify(p);
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* We need to translate between nfs status return values and
|
* We need to translate between nfs status return values and
|
* the local errno values which may not be the same.
|
* the local errno values which may not be the same.
|
*/
|
*/
|
|
|
static struct {
|
static struct {
|
int stat;
|
int stat;
|
int errno;
|
int errno;
|
} nfs_errtbl[] = {
|
} nfs_errtbl[] = {
|
{ NFS_OK, 0 },
|
{ NFS_OK, 0 },
|
{ NFSERR_PERM, EPERM },
|
{ NFSERR_PERM, EPERM },
|
{ NFSERR_NOENT, ENOENT },
|
{ NFSERR_NOENT, ENOENT },
|
{ NFSERR_IO, errno_NFSERR_IO },
|
{ NFSERR_IO, errno_NFSERR_IO },
|
{ NFSERR_NXIO, ENXIO },
|
{ NFSERR_NXIO, ENXIO },
|
{ NFSERR_EAGAIN, EAGAIN },
|
{ NFSERR_EAGAIN, EAGAIN },
|
{ NFSERR_ACCES, EACCES },
|
{ NFSERR_ACCES, EACCES },
|
{ NFSERR_EXIST, EEXIST },
|
{ NFSERR_EXIST, EEXIST },
|
{ NFSERR_XDEV, EXDEV },
|
{ NFSERR_XDEV, EXDEV },
|
{ NFSERR_NODEV, ENODEV },
|
{ NFSERR_NODEV, ENODEV },
|
{ NFSERR_NOTDIR, ENOTDIR },
|
{ NFSERR_NOTDIR, ENOTDIR },
|
{ NFSERR_ISDIR, EISDIR },
|
{ NFSERR_ISDIR, EISDIR },
|
{ NFSERR_INVAL, EINVAL },
|
{ NFSERR_INVAL, EINVAL },
|
{ NFSERR_FBIG, EFBIG },
|
{ NFSERR_FBIG, EFBIG },
|
{ NFSERR_NOSPC, ENOSPC },
|
{ NFSERR_NOSPC, ENOSPC },
|
{ NFSERR_ROFS, EROFS },
|
{ NFSERR_ROFS, EROFS },
|
{ NFSERR_NAMETOOLONG, ENAMETOOLONG },
|
{ NFSERR_NAMETOOLONG, ENAMETOOLONG },
|
{ NFSERR_NOTEMPTY, ENOTEMPTY },
|
{ NFSERR_NOTEMPTY, ENOTEMPTY },
|
{ NFSERR_DQUOT, EDQUOT },
|
{ NFSERR_DQUOT, EDQUOT },
|
{ NFSERR_STALE, ESTALE },
|
{ NFSERR_STALE, ESTALE },
|
#ifdef EWFLUSH
|
#ifdef EWFLUSH
|
{ NFSERR_WFLUSH, EWFLUSH },
|
{ NFSERR_WFLUSH, EWFLUSH },
|
#endif
|
#endif
|
{ -1, EIO }
|
{ -1, EIO }
|
};
|
};
|
|
|
static int nfs_stat_to_errno(int stat)
|
static int nfs_stat_to_errno(int stat)
|
{
|
{
|
int i;
|
int i;
|
|
|
for (i = 0; nfs_errtbl[i].stat != -1; i++) {
|
for (i = 0; nfs_errtbl[i].stat != -1; i++) {
|
if (nfs_errtbl[i].stat == stat)
|
if (nfs_errtbl[i].stat == stat)
|
return nfs_errtbl[i].errno;
|
return nfs_errtbl[i].errno;
|
}
|
}
|
printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
|
printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
|
return nfs_errtbl[i].errno;
|
return nfs_errtbl[i].errno;
|
}
|
}
|
|
|
|
|