proc.c 21 KB
Newer Older
1 2 3
/*
 *  linux/fs/nfs/proc.c
 *
Linus Torvalds's avatar
Linus Torvalds committed
4
 *  Copyright (C) 1992, 1993, 1994  Rick Sladkey
5 6
 *
 *  OS-independent nfs remote procedure call functions
Linus Torvalds's avatar
Linus Torvalds committed
7 8 9 10 11 12 13 14 15 16
 *
 *  Tuned by Alan Cox <A.Cox@swansea.ac.uk> for >3K buffers
 *  so at last we can have decent(ish) throughput off a 
 *  Sun server.
 *
 *  FixMe: We ought to define a sensible small max size for
 *  things like getattr that are tiny packets and use the
 *  old get_free_page stuff with it.
 *
 *  Feel free to fix it and mail me the diffs if it worries you.
17 18 19 20 21 22 23 24
 */

/*
 * Defining NFS_PROC_DEBUG causes a lookup of a file named
 * "xyzzy" to toggle debugging.  Just cd to an NFS-mounted
 * filesystem and type 'ls xyzzy' to turn on debugging.
 */

25
#if 0
26
#define NFS_PROC_DEBUG
27
#endif
28

29
#include <linux/config.h>
30 31 32
#include <linux/param.h>
#include <linux/sched.h>
#include <linux/mm.h>
Linus Torvalds's avatar
Linus Torvalds committed
33
#include <linux/malloc.h>
34 35 36 37
#include <linux/nfs_fs.h>
#include <linux/utsname.h>
#include <linux/errno.h>
#include <linux/string.h>
38
#include <linux/in.h>
39

40
#ifdef NFS_PROC_DEBUG
Linus Torvalds's avatar
Linus Torvalds committed
41

42
static int proc_debug = 0;
Linus Torvalds's avatar
Linus Torvalds committed
43 44 45 46 47 48 49 50 51
#define PRINTK(format, args...) \
	do {						\
		if (proc_debug)				\
			printk(format , ## args);	\
	} while (0)

#else /* !NFS_PROC_DEBUG */

#define PRINTK(format, args...) do ; while (0)
52

Linus Torvalds's avatar
Linus Torvalds committed
53
#endif /* !NFS_PROC_DEBUG */
54

Linus Torvalds's avatar
Linus Torvalds committed
55
static int *nfs_rpc_header(int *p, int procedure, int ruid);
56 57 58
static int *nfs_rpc_verify(int *p);
static int nfs_stat_to_errno(int stat);

Linus Torvalds's avatar
Linus Torvalds committed
59 60 61
/*
 * Our memory allocation and release functions.
 */
Linus Torvalds's avatar
Linus Torvalds committed
62 63
 
#define NFS_SLACK_SPACE		1024	/* Total overkill */ 
Linus Torvalds's avatar
Linus Torvalds committed
64

Linus Torvalds's avatar
Linus Torvalds committed
65
static inline int *nfs_rpc_alloc(int size)
Linus Torvalds's avatar
Linus Torvalds committed
66
{
Linus Torvalds's avatar
Linus Torvalds committed
67 68
	size+=NFS_SLACK_SPACE;		/* Allow for the NFS crap as well as buffer */
	return (int *)kmalloc(size,GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
69 70 71 72
}

static inline void nfs_rpc_free(int *p)
{
Linus Torvalds's avatar
Linus Torvalds committed
73
	kfree((void *)p);
Linus Torvalds's avatar
Linus Torvalds committed
74 75
}

76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
/*
 * Here are a bunch of xdr encode/decode functions that convert
 * between machine dependent and xdr data formats.
 */

static inline int *xdr_encode_fhandle(int *p, struct nfs_fh *fhandle)
{
	*((struct nfs_fh *) p) = *fhandle;
	p += (sizeof (*fhandle) + 3) >> 2;
	return p;
}

static inline int *xdr_decode_fhandle(int *p, struct nfs_fh *fhandle)
{
	*fhandle = *((struct nfs_fh *) p);
	p += (sizeof (*fhandle) + 3) >> 2;
	return p;
}

95
static inline int *xdr_encode_string(int *p, const char *string)
96 97 98 99 100 101 102 103 104 105 106 107
{
	int len, quadlen;
	
	len = strlen(string);
	quadlen = (len + 3) >> 2;
	*p++ = htonl(len);
	memcpy((char *) p, string, len);
	memset(((char *) p) + len, '\0', (quadlen << 2) - len);
	p += quadlen;
	return p;
}

108
static inline int *xdr_decode_string(int *p, char *string, int maxlen)
109
{
110
	unsigned int len;
111 112

	len = ntohl(*p++);
113 114
	if (len > maxlen)
		return NULL;
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
	memcpy(string, (char *) p, len);
	string[len] = '\0';
	p += (len + 3) >> 2;
	return p;
}

static inline int *xdr_encode_data(int *p, char *data, int len)
{
	int quadlen;
	
	quadlen = (len + 3) >> 2;
	*p++ = htonl(len);
	memcpy((char *) p, data, len);
	memset(((char *) p) + len, '\0', (quadlen << 2) - len);
	p += quadlen;
	return p;
}

133
static inline int *xdr_decode_data(int *p, char *data, int *lenp, int maxlen)
134
{
135
	unsigned int len;
136 137

	len = *lenp = ntohl(*p++);
138 139
	if (len > maxlen)
		return NULL;
140 141 142 143 144 145 146
	memcpy(data, (char *) p, len);
	p += (len + 3) >> 2;
	return p;
}

static int *xdr_decode_fattr(int *p, struct nfs_fattr *fattr)
{
Linus Torvalds's avatar
Linus Torvalds committed
147
	fattr->type = (enum nfs_ftype) ntohl(*p++);
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
	fattr->mode = ntohl(*p++);
	fattr->nlink = ntohl(*p++);
	fattr->uid = ntohl(*p++);
	fattr->gid = ntohl(*p++);
	fattr->size = ntohl(*p++);
	fattr->blocksize = ntohl(*p++);
	fattr->rdev = ntohl(*p++);
	fattr->blocks = ntohl(*p++);
	fattr->fsid = ntohl(*p++);
	fattr->fileid = ntohl(*p++);
	fattr->atime.seconds = ntohl(*p++);
	fattr->atime.useconds = ntohl(*p++);
	fattr->mtime.seconds = ntohl(*p++);
	fattr->mtime.useconds = ntohl(*p++);
	fattr->ctime.seconds = ntohl(*p++);
	fattr->ctime.useconds = ntohl(*p++);
	return p;
}

static int *xdr_encode_sattr(int *p, struct nfs_sattr *sattr)
{
	*p++ = htonl(sattr->mode);
	*p++ = htonl(sattr->uid);
	*p++ = htonl(sattr->gid);
	*p++ = htonl(sattr->size);
	*p++ = htonl(sattr->atime.seconds);
	*p++ = htonl(sattr->atime.useconds);
	*p++ = htonl(sattr->mtime.seconds);
	*p++ = htonl(sattr->mtime.useconds);
	return p;
}

static int *xdr_decode_entry(int *p, struct nfs_entry *entry)
{
	entry->fileid = ntohl(*p++);
183 184
	if (!(p = xdr_decode_string(p, entry->name, NFS_MAXNAMLEN)))
		return NULL;
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
	entry->cookie = ntohl(*p++);
	entry->eof = 0;
	return p;
}

static int *xdr_decode_fsinfo(int *p, struct nfs_fsinfo *res)
{
	res->tsize = ntohl(*p++);
	res->bsize = ntohl(*p++);
	res->blocks = ntohl(*p++);
	res->bfree = ntohl(*p++);
	res->bavail = ntohl(*p++);
	return p;
}

Linus Torvalds's avatar
Linus Torvalds committed
200 201 202 203
/*
 * One function for each procedure in the NFS protocol.
 */

204 205 206 207 208
int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
		     struct nfs_fattr *fattr)
{
	int *p, *p0;
	int status;
Linus Torvalds's avatar
Linus Torvalds committed
209
	int ruid = 0;
210 211

	PRINTK("NFS call  getattr\n");
Linus Torvalds's avatar
Linus Torvalds committed
212
	if (!(p0 = nfs_rpc_alloc(server->rsize)))
Linus Torvalds's avatar
Linus Torvalds committed
213
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
214 215
retry:
	p = nfs_rpc_header(p0, NFSPROC_GETATTR, ruid);
216 217
	p = xdr_encode_fhandle(p, fhandle);
	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
218
		nfs_rpc_free(p0);
219 220 221 222 223 224 225 226
		return status;
	}
	if (!(p = nfs_rpc_verify(p0)))
		status = NFSERR_IO;
	else if ((status = ntohl(*p++)) == NFS_OK) {
		p = xdr_decode_fattr(p, fattr);
		PRINTK("NFS reply getattr\n");
	}
Linus Torvalds's avatar
Linus Torvalds committed
227
	else {
Linus Torvalds's avatar
Linus Torvalds committed
228
		if (!ruid && current->fsuid == 0 && current->uid != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
229 230 231
			ruid = 1;
			goto retry;
		}
232
		PRINTK("NFS reply getattr failed = %d\n", status);
Linus Torvalds's avatar
Linus Torvalds committed
233
	}
Linus Torvalds's avatar
Linus Torvalds committed
234
	nfs_rpc_free(p0);
235 236 237 238 239 240 241 242
	return -nfs_stat_to_errno(status);
}

int nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle,
		     struct nfs_sattr *sattr, struct nfs_fattr *fattr)
{
	int *p, *p0;
	int status;
Linus Torvalds's avatar
Linus Torvalds committed
243
	int ruid = 0;
244 245

	PRINTK("NFS call  setattr\n");
Linus Torvalds's avatar
Linus Torvalds committed
246
	if (!(p0 = nfs_rpc_alloc(server->wsize)))
Linus Torvalds's avatar
Linus Torvalds committed
247
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
248 249
retry:
	p = nfs_rpc_header(p0, NFSPROC_SETATTR, ruid);
250 251 252
	p = xdr_encode_fhandle(p, fhandle);
	p = xdr_encode_sattr(p, sattr);
	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
253
		nfs_rpc_free(p0);
254 255 256 257 258 259 260 261
		return status;
	}
	if (!(p = nfs_rpc_verify(p0)))
		status = NFSERR_IO;
	else if ((status = ntohl(*p++)) == NFS_OK) {
		p = xdr_decode_fattr(p, fattr);
		PRINTK("NFS reply setattr\n");
	}
Linus Torvalds's avatar
Linus Torvalds committed
262
	else {
Linus Torvalds's avatar
Linus Torvalds committed
263
		if (!ruid && current->fsuid == 0 && current->uid != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
264 265 266
			ruid = 1;
			goto retry;
		}
267
		PRINTK("NFS reply setattr failed = %d\n", status);
Linus Torvalds's avatar
Linus Torvalds committed
268
	}
Linus Torvalds's avatar
Linus Torvalds committed
269
	nfs_rpc_free(p0);
270 271 272
	return -nfs_stat_to_errno(status);
}

273
int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, const char *name,
274 275 276 277
		    struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
	int *p, *p0;
	int status;
Linus Torvalds's avatar
Linus Torvalds committed
278
	int ruid = 0;
279 280 281 282 283 284

	PRINTK("NFS call  lookup %s\n", name);
#ifdef NFS_PROC_DEBUG
	if (!strcmp(name, "xyzzy"))
		proc_debug = 1 - proc_debug;
#endif
Linus Torvalds's avatar
Linus Torvalds committed
285
	if (!(p0 = nfs_rpc_alloc(server->rsize)))
Linus Torvalds's avatar
Linus Torvalds committed
286
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
287 288
retry:
	p = nfs_rpc_header(p0, NFSPROC_LOOKUP, ruid);
289 290 291
	p = xdr_encode_fhandle(p, dir);
	p = xdr_encode_string(p, name);
	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
292
		nfs_rpc_free(p0);
293 294 295 296 297 298 299 300 301
		return status;
	}
	if (!(p = nfs_rpc_verify(p0)))
		status = NFSERR_IO;
	else if ((status = ntohl(*p++)) == NFS_OK) {
		p = xdr_decode_fhandle(p, fhandle);
		p = xdr_decode_fattr(p, fattr);
		PRINTK("NFS reply lookup\n");
	}
Linus Torvalds's avatar
Linus Torvalds committed
302
	else {
Linus Torvalds's avatar
Linus Torvalds committed
303
		if (!ruid && current->fsuid == 0 && current->uid != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
304 305 306
			ruid = 1;
			goto retry;
		}
307
		PRINTK("NFS reply lookup failed = %d\n", status);
Linus Torvalds's avatar
Linus Torvalds committed
308
	}
Linus Torvalds's avatar
Linus Torvalds committed
309
	nfs_rpc_free(p0);
310 311 312 313 314 315 316 317
	return -nfs_stat_to_errno(status);
}

int nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle,
		      char *res)
{
	int *p, *p0;
	int status;
Linus Torvalds's avatar
Linus Torvalds committed
318
	int ruid = 0;
319 320

	PRINTK("NFS call  readlink\n");
Linus Torvalds's avatar
Linus Torvalds committed
321
	if (!(p0 = nfs_rpc_alloc(server->rsize)))
Linus Torvalds's avatar
Linus Torvalds committed
322
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
323 324
retry:
	p = nfs_rpc_header(p0, NFSPROC_READLINK, ruid);
325 326
	p = xdr_encode_fhandle(p, fhandle);
	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
327
		nfs_rpc_free(p0);
328 329 330 331 332
		return status;
	}
	if (!(p = nfs_rpc_verify(p0)))
		status = NFSERR_IO;
	else if ((status = ntohl(*p++)) == NFS_OK) {
333 334 335 336 337 338 339
		if (!(p = xdr_decode_string(p, res, NFS_MAXPATHLEN))) {
			printk("nfs_proc_readlink: giant pathname\n");
			status = NFSERR_IO;
		}
		else
			PRINTK("NFS reply readlink %s\n", res);
	}
Linus Torvalds's avatar
Linus Torvalds committed
340
	else {
Linus Torvalds's avatar
Linus Torvalds committed
341
		if (!ruid && current->fsuid == 0 && current->uid != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
342 343 344
			ruid = 1;
			goto retry;
		}
345
		PRINTK("NFS reply readlink failed = %d\n", status);
Linus Torvalds's avatar
Linus Torvalds committed
346
	}
Linus Torvalds's avatar
Linus Torvalds committed
347
	nfs_rpc_free(p0);
348 349 350 351 352 353 354 355
	return -nfs_stat_to_errno(status);
}

int nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle,
		  int offset, int count, char *data, struct nfs_fattr *fattr)
{
	int *p, *p0;
	int status;
Linus Torvalds's avatar
Linus Torvalds committed
356
	int ruid = 0;
357 358 359
	int len = 0; /* = 0 is for gcc */

	PRINTK("NFS call  read %d @ %d\n", count, offset);
Linus Torvalds's avatar
Linus Torvalds committed
360
	if (!(p0 = nfs_rpc_alloc(server->rsize)))
Linus Torvalds's avatar
Linus Torvalds committed
361
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
362 363
retry:
	p = nfs_rpc_header(p0, NFSPROC_READ, ruid);
364 365 366 367 368
	p = xdr_encode_fhandle(p, fhandle);
	*p++ = htonl(offset);
	*p++ = htonl(count);
	*p++ = htonl(count); /* traditional, could be any value */
	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
369
		nfs_rpc_free(p0);
370 371 372 373 374 375
		return status;
	}
	if (!(p = nfs_rpc_verify(p0)))
		status = NFSERR_IO;
	else if ((status = ntohl(*p++)) == NFS_OK) {
		p = xdr_decode_fattr(p, fattr);
376 377 378 379 380 381 382
		if (!(p = xdr_decode_data(p, data, &len, count))) {
			printk("nfs_proc_read: giant data size\n"); 
			status = NFSERR_IO;
		}
		else
			PRINTK("NFS reply read %d\n", len);
	}
Linus Torvalds's avatar
Linus Torvalds committed
383
	else {
Linus Torvalds's avatar
Linus Torvalds committed
384
		if (!ruid && current->fsuid == 0 && current->uid != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
385 386 387
			ruid = 1;
			goto retry;
		}
388
		PRINTK("NFS reply read failed = %d\n", status);
Linus Torvalds's avatar
Linus Torvalds committed
389
	}
Linus Torvalds's avatar
Linus Torvalds committed
390
	nfs_rpc_free(p0);
391 392 393 394 395 396 397 398
	return (status == NFS_OK) ? len : -nfs_stat_to_errno(status);
}

int nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle,
		   int offset, int count, char *data, struct nfs_fattr *fattr)
{
	int *p, *p0;
	int status;
Linus Torvalds's avatar
Linus Torvalds committed
399
	int ruid = 0;
400 401

	PRINTK("NFS call  write %d @ %d\n", count, offset);
Linus Torvalds's avatar
Linus Torvalds committed
402
	if (!(p0 = nfs_rpc_alloc(server->wsize)))
Linus Torvalds's avatar
Linus Torvalds committed
403
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
404 405
retry:
	p = nfs_rpc_header(p0, NFSPROC_WRITE, ruid);
406 407 408 409 410 411
	p = xdr_encode_fhandle(p, fhandle);
	*p++ = htonl(offset); /* traditional, could be any value */
	*p++ = htonl(offset);
	*p++ = htonl(count); /* traditional, could be any value */
	p = xdr_encode_data(p, data, count);
	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
412
		nfs_rpc_free(p0);
413 414 415 416 417 418 419 420
		return status;
	}
	if (!(p = nfs_rpc_verify(p0)))
		status = NFSERR_IO;
	else if ((status = ntohl(*p++)) == NFS_OK) {
		p = xdr_decode_fattr(p, fattr);
		PRINTK("NFS reply write\n");
	}
Linus Torvalds's avatar
Linus Torvalds committed
421
	else {
Linus Torvalds's avatar
Linus Torvalds committed
422
		if (!ruid && current->fsuid == 0 && current->uid != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
423 424 425
			ruid = 1;
			goto retry;
		}
426
		PRINTK("NFS reply write failed = %d\n", status);
Linus Torvalds's avatar
Linus Torvalds committed
427
	}
Linus Torvalds's avatar
Linus Torvalds committed
428
	nfs_rpc_free(p0);
429 430 431 432
	return -nfs_stat_to_errno(status);
}

int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
433
		    const char *name, struct nfs_sattr *sattr,
434 435 436 437
		    struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
	int *p, *p0;
	int status;
Linus Torvalds's avatar
Linus Torvalds committed
438
	int ruid = 0;
439 440

	PRINTK("NFS call  create %s\n", name);
Linus Torvalds's avatar
Linus Torvalds committed
441
	if (!(p0 = nfs_rpc_alloc(server->wsize)))
Linus Torvalds's avatar
Linus Torvalds committed
442
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
443 444
retry:
	p = nfs_rpc_header(p0, NFSPROC_CREATE, ruid);
445 446 447 448
	p = xdr_encode_fhandle(p, dir);
	p = xdr_encode_string(p, name);
	p = xdr_encode_sattr(p, sattr);
	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
449
		nfs_rpc_free(p0);
450 451 452 453 454 455 456 457 458
		return status;
	}
	if (!(p = nfs_rpc_verify(p0)))
		status = NFSERR_IO;
	else if ((status = ntohl(*p++)) == NFS_OK) {
		p = xdr_decode_fhandle(p, fhandle);
		p = xdr_decode_fattr(p, fattr);
		PRINTK("NFS reply create\n");
	}
Linus Torvalds's avatar
Linus Torvalds committed
459
	else {
Linus Torvalds's avatar
Linus Torvalds committed
460
		if (!ruid && current->fsuid == 0 && current->uid != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
461 462 463
			ruid = 1;
			goto retry;
		}
464
		PRINTK("NFS reply create failed = %d\n", status);
Linus Torvalds's avatar
Linus Torvalds committed
465
	}
Linus Torvalds's avatar
Linus Torvalds committed
466
	nfs_rpc_free(p0);
467 468 469
	return -nfs_stat_to_errno(status);
}

470
int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir, const char *name)
471 472 473
{
	int *p, *p0;
	int status;
Linus Torvalds's avatar
Linus Torvalds committed
474
	int ruid = 0;
475 476

	PRINTK("NFS call  remove %s\n", name);
Linus Torvalds's avatar
Linus Torvalds committed
477
	if (!(p0 = nfs_rpc_alloc(server->wsize)))
Linus Torvalds's avatar
Linus Torvalds committed
478
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
479 480
retry:
	p = nfs_rpc_header(p0, NFSPROC_REMOVE, ruid);
481 482 483
	p = xdr_encode_fhandle(p, dir);
	p = xdr_encode_string(p, name);
	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
484
		nfs_rpc_free(p0);
485 486 487 488 489 490 491
		return status;
	}
	if (!(p = nfs_rpc_verify(p0)))
		status = NFSERR_IO;
	else if ((status = ntohl(*p++)) == NFS_OK) {
		PRINTK("NFS reply remove\n");
	}
Linus Torvalds's avatar
Linus Torvalds committed
492
	else {
Linus Torvalds's avatar
Linus Torvalds committed
493
		if (!ruid && current->fsuid == 0 && current->uid != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
494 495 496
			ruid = 1;
			goto retry;
		}
497
		PRINTK("NFS reply remove failed = %d\n", status);
Linus Torvalds's avatar
Linus Torvalds committed
498
	}
Linus Torvalds's avatar
Linus Torvalds committed
499
	nfs_rpc_free(p0);
500 501 502 503
	return -nfs_stat_to_errno(status);
}

int nfs_proc_rename(struct nfs_server *server,
504 505
		    struct nfs_fh *old_dir, const char *old_name,
		    struct nfs_fh *new_dir, const char *new_name)
506 507 508
{
	int *p, *p0;
	int status;
Linus Torvalds's avatar
Linus Torvalds committed
509
	int ruid = 0;
510 511

	PRINTK("NFS call  rename %s -> %s\n", old_name, new_name);
Linus Torvalds's avatar
Linus Torvalds committed
512
	if (!(p0 = nfs_rpc_alloc(server->wsize)))
Linus Torvalds's avatar
Linus Torvalds committed
513
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
514 515
retry:
	p = nfs_rpc_header(p0, NFSPROC_RENAME, ruid);
516 517 518 519 520
	p = xdr_encode_fhandle(p, old_dir);
	p = xdr_encode_string(p, old_name);
	p = xdr_encode_fhandle(p, new_dir);
	p = xdr_encode_string(p, new_name);
	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
521
		nfs_rpc_free(p0);
522 523 524 525 526 527 528
		return status;
	}
	if (!(p = nfs_rpc_verify(p0)))
		status = NFSERR_IO;
	else if ((status = ntohl(*p++)) == NFS_OK) {
		PRINTK("NFS reply rename\n");
	}
Linus Torvalds's avatar
Linus Torvalds committed
529
	else {
Linus Torvalds's avatar
Linus Torvalds committed
530
		if (!ruid && current->fsuid == 0 && current->uid != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
531 532 533
			ruid = 1;
			goto retry;
		}
534
		PRINTK("NFS reply rename failed = %d\n", status);
Linus Torvalds's avatar
Linus Torvalds committed
535
	}
Linus Torvalds's avatar
Linus Torvalds committed
536
	nfs_rpc_free(p0);
537 538 539 540
	return -nfs_stat_to_errno(status);
}

int nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
541
		  struct nfs_fh *dir, const char *name)
542 543 544
{
	int *p, *p0;
	int status;
Linus Torvalds's avatar
Linus Torvalds committed
545
	int ruid = 0;
546 547

	PRINTK("NFS call  link %s\n", name);
Linus Torvalds's avatar
Linus Torvalds committed
548
	if (!(p0 = nfs_rpc_alloc(server->wsize)))
Linus Torvalds's avatar
Linus Torvalds committed
549
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
550 551
retry:
	p = nfs_rpc_header(p0, NFSPROC_LINK, ruid);
552 553 554 555
	p = xdr_encode_fhandle(p, fhandle);
	p = xdr_encode_fhandle(p, dir);
	p = xdr_encode_string(p, name);
	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
556
		nfs_rpc_free(p0);
557 558 559 560 561 562 563
		return status;
	}
	if (!(p = nfs_rpc_verify(p0)))
		status = NFSERR_IO;
	else if ((status = ntohl(*p++)) == NFS_OK) {
		PRINTK("NFS reply link\n");
	}
Linus Torvalds's avatar
Linus Torvalds committed
564
	else {
Linus Torvalds's avatar
Linus Torvalds committed
565
		if (!ruid && current->fsuid == 0 && current->uid != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
566 567 568
			ruid = 1;
			goto retry;
		}
569
		PRINTK("NFS reply link failed = %d\n", status);
Linus Torvalds's avatar
Linus Torvalds committed
570
	}
Linus Torvalds's avatar
Linus Torvalds committed
571
	nfs_rpc_free(p0);
572 573 574 575
	return -nfs_stat_to_errno(status);
}

int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
576
		     const char *name, const char *path, struct nfs_sattr *sattr)
577 578 579
{
	int *p, *p0;
	int status;
Linus Torvalds's avatar
Linus Torvalds committed
580
	int ruid = 0;
581 582

	PRINTK("NFS call  symlink %s -> %s\n", name, path);
Linus Torvalds's avatar
Linus Torvalds committed
583
	if (!(p0 = nfs_rpc_alloc(server->wsize)))
Linus Torvalds's avatar
Linus Torvalds committed
584
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
585 586
retry:
	p = nfs_rpc_header(p0, NFSPROC_SYMLINK, ruid);
587 588 589 590 591
	p = xdr_encode_fhandle(p, dir);
	p = xdr_encode_string(p, name);
	p = xdr_encode_string(p, path);
	p = xdr_encode_sattr(p, sattr);
	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
592
		nfs_rpc_free(p0);
593 594 595 596 597 598 599
		return status;
	}
	if (!(p = nfs_rpc_verify(p0)))
		status = NFSERR_IO;
	else if ((status = ntohl(*p++)) == NFS_OK) {
		PRINTK("NFS reply symlink\n");
	}
Linus Torvalds's avatar
Linus Torvalds committed
600
	else {
Linus Torvalds's avatar
Linus Torvalds committed
601
		if (!ruid && current->fsuid == 0 && current->uid != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
602 603 604
			ruid = 1;
			goto retry;
		}
605
		PRINTK("NFS reply symlink failed = %d\n", status);
Linus Torvalds's avatar
Linus Torvalds committed
606
	}
Linus Torvalds's avatar
Linus Torvalds committed
607
	nfs_rpc_free(p0);
608 609 610 611
	return -nfs_stat_to_errno(status);
}

int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
612
		   const char *name, struct nfs_sattr *sattr,
613 614 615 616
		   struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
	int *p, *p0;
	int status;
Linus Torvalds's avatar
Linus Torvalds committed
617
	int ruid = 0;
618 619

	PRINTK("NFS call  mkdir %s\n", name);
Linus Torvalds's avatar
Linus Torvalds committed
620
	if (!(p0 = nfs_rpc_alloc(server->wsize)))
Linus Torvalds's avatar
Linus Torvalds committed
621
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
622 623
retry:
	p = nfs_rpc_header(p0, NFSPROC_MKDIR, ruid);
624 625 626 627
	p = xdr_encode_fhandle(p, dir);
	p = xdr_encode_string(p, name);
	p = xdr_encode_sattr(p, sattr);
	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
628
		nfs_rpc_free(p0);
629 630 631 632 633 634 635 636 637
		return status;
	}
	if (!(p = nfs_rpc_verify(p0)))
		status = NFSERR_IO;
	else if ((status = ntohl(*p++)) == NFS_OK) {
		p = xdr_decode_fhandle(p, fhandle);
		p = xdr_decode_fattr(p, fattr);
		PRINTK("NFS reply mkdir\n");
	}
Linus Torvalds's avatar
Linus Torvalds committed
638
	else {
Linus Torvalds's avatar
Linus Torvalds committed
639
		if (!ruid && current->fsuid == 0 && current->uid != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
640 641 642
			ruid = 1;
			goto retry;
		}
643
		PRINTK("NFS reply mkdir failed = %d\n", status);
Linus Torvalds's avatar
Linus Torvalds committed
644
	}
Linus Torvalds's avatar
Linus Torvalds committed
645
	nfs_rpc_free(p0);
646 647 648
	return -nfs_stat_to_errno(status);
}

649
int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name)
650 651 652
{
	int *p, *p0;
	int status;
Linus Torvalds's avatar
Linus Torvalds committed
653
	int ruid = 0;
654 655

	PRINTK("NFS call  rmdir %s\n", name);
Linus Torvalds's avatar
Linus Torvalds committed
656
	if (!(p0 = nfs_rpc_alloc(server->wsize)))
Linus Torvalds's avatar
Linus Torvalds committed
657
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
658 659
retry:
	p = nfs_rpc_header(p0, NFSPROC_RMDIR, ruid);
660 661 662
	p = xdr_encode_fhandle(p, dir);
	p = xdr_encode_string(p, name);
	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
663
		nfs_rpc_free(p0);
664 665 666 667 668 669 670
		return status;
	}
	if (!(p = nfs_rpc_verify(p0)))
		status = NFSERR_IO;
	else if ((status = ntohl(*p++)) == NFS_OK) {
		PRINTK("NFS reply rmdir\n");
	}
Linus Torvalds's avatar
Linus Torvalds committed
671
	else {
Linus Torvalds's avatar
Linus Torvalds committed
672
		if (!ruid && current->fsuid == 0 && current->uid != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
673 674 675
			ruid = 1;
			goto retry;
		}
676
		PRINTK("NFS reply rmdir failed = %d\n", status);
Linus Torvalds's avatar
Linus Torvalds committed
677
	}
Linus Torvalds's avatar
Linus Torvalds committed
678
	nfs_rpc_free(p0);
679 680 681 682 683 684 685 686
	return -nfs_stat_to_errno(status);
}

int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
		     int cookie, int count, struct nfs_entry *entry)
{
	int *p, *p0;
	int status;
Linus Torvalds's avatar
Linus Torvalds committed
687
	int ruid = 0;
688 689 690 691 692 693
	int i = 0; /* = 0 is for gcc */
	int size;
	int eof;

	PRINTK("NFS call  readdir %d @ %d\n", count, cookie);
	size = server->rsize;
Linus Torvalds's avatar
Linus Torvalds committed
694
	if (!(p0 = nfs_rpc_alloc(server->rsize)))
Linus Torvalds's avatar
Linus Torvalds committed
695
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
696 697
retry:
	p = nfs_rpc_header(p0, NFSPROC_READDIR, ruid);
698 699 700 701
	p = xdr_encode_fhandle(p, fhandle);
	*p++ = htonl(cookie);
	*p++ = htonl(size);
	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
702
		nfs_rpc_free(p0);
703 704 705 706 707
		return status;
	}
	if (!(p = nfs_rpc_verify(p0)))
		status = NFSERR_IO;
	else if ((status = ntohl(*p++)) == NFS_OK) {
708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
		for (i = 0; i < count && *p++; i++) {
			if (!(p = xdr_decode_entry(p, entry++)))
				break;
		}
		if (!p) {
			printk("nfs_proc_readdir: giant filename\n");
			status = NFSERR_IO;
		}
		else {
			eof = (i == count && !*p++ && *p++)
			      || (i < count && *p++);
			if (eof && i)
				entry[-1].eof = 1;
			PRINTK("NFS reply readdir %d %s\n", i,
			       eof ? "eof" : "");
		}
	}
Linus Torvalds's avatar
Linus Torvalds committed
725
	else {
Linus Torvalds's avatar
Linus Torvalds committed
726
		if (!ruid && current->fsuid == 0 && current->uid != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
727 728 729
			ruid = 1;
			goto retry;
		}
730
		PRINTK("NFS reply readdir failed = %d\n", status);
Linus Torvalds's avatar
Linus Torvalds committed
731
	}
Linus Torvalds's avatar
Linus Torvalds committed
732
	nfs_rpc_free(p0);
733 734 735 736 737 738 739 740
	return (status == NFS_OK) ? i : -nfs_stat_to_errno(status);
}

int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
		    struct nfs_fsinfo *res)
{
	int *p, *p0;
	int status;
Linus Torvalds's avatar
Linus Torvalds committed
741
	int ruid = 0;
742 743

	PRINTK("NFS call  statfs\n");
Linus Torvalds's avatar
Linus Torvalds committed
744
	if (!(p0 = nfs_rpc_alloc(server->rsize)))
Linus Torvalds's avatar
Linus Torvalds committed
745
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
746 747
retry:
	p = nfs_rpc_header(p0, NFSPROC_STATFS, ruid);
748 749
	p = xdr_encode_fhandle(p, fhandle);
	if ((status = nfs_rpc_call(server, p0, p)) < 0) {
Linus Torvalds's avatar
Linus Torvalds committed
750
		nfs_rpc_free(p0);
751 752 753 754 755 756 757 758
		return status;
	}
	if (!(p = nfs_rpc_verify(p0)))
		status = NFSERR_IO;
	else if ((status = ntohl(*p++)) == NFS_OK) {
		p = xdr_decode_fsinfo(p, res);
		PRINTK("NFS reply statfs\n");
	}
Linus Torvalds's avatar
Linus Torvalds committed
759
	else {
Linus Torvalds's avatar
Linus Torvalds committed
760
		if (!ruid && current->fsuid == 0 && current->uid != 0) {
Linus Torvalds's avatar
Linus Torvalds committed
761 762 763
			ruid = 1;
			goto retry;
		}
764
		PRINTK("NFS reply statfs failed = %d\n", status);
Linus Torvalds's avatar
Linus Torvalds committed
765
	}
Linus Torvalds's avatar
Linus Torvalds committed
766
	nfs_rpc_free(p0);
767 768 769 770 771 772 773
	return -nfs_stat_to_errno(status);
}

/*
 * Here are a few RPC-assist functions.
 */

Linus Torvalds's avatar
Linus Torvalds committed
774
static int *nfs_rpc_header(int *p, int procedure, int ruid)
775 776 777 778
{
	int *p1, *p2;
	int i;
	static int xid = 0;
779
	unsigned char *sys = (unsigned char *) system_utsname.nodename;
780 781 782 783 784 785 786 787 788 789 790 791 792 793

	if (xid == 0) {
		xid = CURRENT_TIME;
		xid ^= (sys[3]<<24) | (sys[2]<<16) | (sys[1]<<8) | sys[0];
	}
	*p++ = htonl(++xid);
	*p++ = htonl(RPC_CALL);
	*p++ = htonl(RPC_VERSION);
	*p++ = htonl(NFS_PROGRAM);
	*p++ = htonl(NFS_VERSION);
	*p++ = htonl(procedure);
	*p++ = htonl(RPC_AUTH_UNIX);
	p1 = p++;
	*p++ = htonl(CURRENT_TIME); /* traditional, could be anything */
794
	p = xdr_encode_string(p, (char *) sys);
Linus Torvalds's avatar
Linus Torvalds committed
795
	*p++ = htonl(ruid ? current->uid : current->fsuid);
796 797 798 799 800 801 802 803 804 805 806 807 808
	*p++ = htonl(current->egid);
	p2 = p++;
	for (i = 0; i < 16 && i < NGROUPS && current->groups[i] != NOGROUP; i++)
		*p++ = htonl(current->groups[i]);
	*p2 = htonl(i);
	*p1 = htonl((p - (p1 + 1)) << 2);
	*p++ = htonl(RPC_AUTH_NULL);
	*p++ = htonl(0);
	return p;
}

static int *nfs_rpc_verify(int *p)
{
809
	unsigned int n;
810 811

	p++;
812 813
	if ((n = ntohl(*p++)) != RPC_REPLY) {
		printk("nfs_rpc_verify: not an RPC reply: %d\n", n);
814
		return NULL;
815
	}
816 817
	if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
		printk("nfs_rpc_verify: RPC call rejected: %d\n", n);
818 819 820 821 822 823 824 825
		return NULL;
	}
	switch (n = ntohl(*p++)) {
	case RPC_AUTH_NULL: case RPC_AUTH_UNIX: case RPC_AUTH_SHORT:
		break;
	default:
		printk("nfs_rpc_verify: bad RPC authentication type: %d\n", n);
		return NULL;
826
	}
827 828 829
	if ((n = ntohl(*p++)) > 400) {
		printk("nfs_rpc_verify: giant auth size\n");
		return NULL;
830 831
	}
	p += (n + 3) >> 2;
832 833
	if ((n = ntohl(*p++)) != RPC_SUCCESS) {
		printk("nfs_rpc_verify: RPC call failed: %d\n", n);
834
		return NULL;
835 836 837 838 839 840 841 842 843 844 845 846 847 848
	}
	return p;
}
	
/*
 * We need to translate between nfs status return values and
 * the local errno values which may not be the same.
 */

#ifndef EDQUOT
#define EDQUOT	ENOSPC
#endif

static struct {
849
	int stat;
850 851
	int errno;
} nfs_errtbl[] = {
852 853 854 855 856 857 858 859 860 861
	{ NFS_OK,		0		},
	{ NFSERR_PERM,		EPERM		},
	{ NFSERR_NOENT,		ENOENT		},
	{ NFSERR_IO,		EIO		},
	{ NFSERR_NXIO,		ENXIO		},
	{ NFSERR_ACCES,		EACCES		},
	{ NFSERR_EXIST,		EEXIST		},
	{ NFSERR_NODEV,		ENODEV		},
	{ NFSERR_NOTDIR,	ENOTDIR		},
	{ NFSERR_ISDIR,		EISDIR		},
Linus Torvalds's avatar
Linus Torvalds committed
862
	{ NFSERR_INVAL,		EINVAL		},
863 864 865 866 867 868 869 870 871 872 873
	{ NFSERR_FBIG,		EFBIG		},
	{ NFSERR_NOSPC,		ENOSPC		},
	{ NFSERR_ROFS,		EROFS		},
	{ NFSERR_NAMETOOLONG,	ENAMETOOLONG	},
	{ NFSERR_NOTEMPTY,	ENOTEMPTY	},
	{ NFSERR_DQUOT,		EDQUOT		},
	{ NFSERR_STALE,		ESTALE		},
#ifdef EWFLUSH
	{ NFSERR_WFLUSH,	EWFLUSH		},
#endif
	{ -1,			EIO		}
874 875 876 877 878 879 880
};

static int nfs_stat_to_errno(int stat)
{
	int i;

	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
881 882
		if (nfs_errtbl[i].stat == stat)
			return nfs_errtbl[i].errno;
883
	}
884 885
	printk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
	return nfs_errtbl[i].errno;
886 887
}