diff options
Diffstat (limited to 'fs/nfs.c')
-rw-r--r-- | fs/nfs.c | 265 |
1 files changed, 191 insertions, 74 deletions
@@ -8,9 +8,6 @@ * Based on U-Boot NFS code which is based on NetBSD code with * major changes to support nfs3. * - * See file CREDITS for list of people who contributed to this - * project. - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. @@ -29,7 +26,6 @@ #include <errno.h> #include <libgen.h> #include <fcntl.h> -#include <fs.h> #include <init.h> #include <linux/stat.h> #include <linux/err.h> @@ -122,8 +118,8 @@ struct rpc_reply { uint32_t data[0]; }; -#define NFS_TIMEOUT (2 * SECOND) -#define NFS_MAX_RESEND 5 +#define NFS_TIMEOUT (100 * MSECOND) +#define NFS_MAX_RESEND 100 struct nfs_fh { unsigned short size; @@ -131,6 +127,7 @@ struct nfs_fh { }; struct packet { + struct list_head list; int len; char data[]; }; @@ -145,7 +142,7 @@ struct nfs_priv { unsigned manual_nfs_port:1; uint32_t rpc_id; struct nfs_fh rootfh; - struct packet *nfs_packet; + struct list_head packets; }; struct file_priv { @@ -175,10 +172,6 @@ static void nfs_set_fh(struct inode *inode, struct nfs_fh *fh) static uint64_t nfs_timer_start; -static int nfs_state; -#define STATE_DONE 1 -#define STATE_START 2 - /* * common types used in more than one request: * @@ -269,6 +262,74 @@ struct nfs_dir { struct nfs_fh fh; }; +struct nfserror { + int ne; + int e; + const char *name; +}; + +static struct nfserror nfserrors[] = { + { .ne = NFS3ERR_PERM, .e = EPERM }, + { .ne = NFS3ERR_NOENT, .e = ENOENT }, + { .ne = NFS3ERR_IO, .e = EIO }, + { .ne = NFS3ERR_NXIO, .e = ENXIO }, + { .ne = NFS3ERR_ACCES, .e = EACCES }, + { .ne = NFS3ERR_EXIST, .e = EEXIST }, + { .ne = NFS3ERR_XDEV, .e = EXDEV }, + { .ne = NFS3ERR_NODEV, .e = ENODEV }, + { .ne = NFS3ERR_NOTDIR, .e = ENOTDIR }, + { .ne = NFS3ERR_ISDIR, .e = EISDIR }, + { .ne = NFS3ERR_INVAL, .e = EINVAL }, + { .ne = NFS3ERR_FBIG, .e = EFBIG }, + { .ne = NFS3ERR_NOSPC, .e = ENOSPC }, + { .ne = NFS3ERR_ROFS, .e = EROFS }, + { .ne = NFS3ERR_MLINK, .e = EMLINK }, + { .ne = NFS3ERR_NAMETOOLONG, .e = ENAMETOOLONG }, + { .ne = NFS3ERR_NOTEMPTY, .e = ENOTEMPTY }, + { .ne = NFS3ERR_DQUOT, .e = EDQUOT }, + { .ne = NFS3ERR_STALE, .e = ESTALE }, + { .ne = NFS3ERR_REMOTE, .e = EREMOTE }, + { .ne = NFS3ERR_NOTSUPP, .e = EOPNOTSUPP }, + { .ne = NFS3ERR_BADHANDLE, .e = EINVAL, .name = "BADHANDLE"}, + { .ne = NFS3ERR_NOT_SYNC, .e = EINVAL, .name = "NOT_SYNC" }, + { .ne = NFS3ERR_BAD_COOKIE, .e = EINVAL, .name = "BAD_COOKIE" }, + { .ne = NFS3ERR_TOOSMALL, .e = EINVAL, .name = "TOOSMALL" }, + { .ne = NFS3ERR_SERVERFAULT, .e = EINVAL, .name = "SERVERFAULT" }, + { .ne = NFS3ERR_BADTYPE, .e = EINVAL, .name = "BADTYPE" }, + { .ne = NFS3ERR_JUKEBOX, .e = EINVAL, .name = "JUKEBOX" }, +}; + +static const char *nfserrstr(u32 nfserror, int *errcode) +{ + static char str[32]; + int i; + + /* + * Most NFS errors have a corresponding POSIX error code. But not all of + * them have one, so some must be mapped to a different code here. + */ + for (i = 0; i < ARRAY_SIZE(nfserrors); i++) { + struct nfserror *err = &nfserrors[i]; + + if (nfserror == err->ne) { + if (errcode) + *errcode = -err->e; + + if (err->name) { + snprintf(str, sizeof(str), "NFS3ERR_%s", err->name); + return str; + } else + return strerror(err->e); + } + } + + if (errcode) + *errcode = -EINVAL; + + snprintf(str, sizeof(str), "Unknown NFS error %d", nfserror); + return str; +} + static void xdr_init(struct xdr_stream *stream, void *buf, int len) { stream->p = stream->buf = buf; @@ -366,18 +427,10 @@ static int rpc_check_reply(struct packet *pkt, int rpc_prog, *nfserr = 0; - if (!pkt) - return -EAGAIN; - memcpy(&rpc, pkt->data, sizeof(rpc)); - if (ntoh32(rpc.id) != rpc_id) { - if (rpc_id - ntoh32(rpc.id) == 1) - /* stale packet, wait a bit longer */ - return 0; - - return -EINVAL; - } + if (ntoh32(rpc.id) != rpc_id) + return -EAGAIN; if (rpc.rstatus || rpc.verifier || @@ -392,11 +445,17 @@ static int rpc_check_reply(struct packet *pkt, int rpc_prog, *nfserr = ntoh32(net_read_uint32(data)); *nfserr = -*nfserr; - debug("%s: state: %d, err %d\n", __func__, nfs_state, *nfserr); + debug("%s: err %d\n", __func__, *nfserr); return 0; } +static void nfs_free_packet(struct packet *packet) +{ + list_del(&packet->list); + free(packet); +} + /* * rpc_req - synchronous RPC request */ @@ -409,6 +468,7 @@ static struct packet *rpc_req(struct nfs_priv *npriv, int rpc_prog, unsigned char *payload = net_udp_get_payload(npriv->con); int nfserr; int tries = 0; + struct packet *packet; npriv->rpc_id++; @@ -453,12 +513,7 @@ again: nfs_timer_start = get_time_ns(); - nfs_state = STATE_START; - - while (nfs_state != STATE_DONE) { - if (ctrlc()) - return ERR_PTR(-EINTR); - + while (1) { net_poll(); if (is_timeout(nfs_timer_start, NFS_TIMEOUT)) { @@ -468,19 +523,28 @@ again: goto again; } - ret = rpc_check_reply(npriv->nfs_packet, rpc_prog, + if (list_empty(&npriv->packets)) + continue; + + packet = list_first_entry(&npriv->packets, struct packet, list); + + ret = rpc_check_reply(packet, rpc_prog, npriv->rpc_id, &nfserr); - if (!ret) { + if (ret == -EAGAIN) { + nfs_free_packet(packet); + continue; + } else if (ret) { + nfs_free_packet(packet); + return ERR_PTR(ret); + } else { if (rpc_prog == PROG_NFS && nfserr) { - free(npriv->nfs_packet); + nfs_free_packet(packet); return ERR_PTR(nfserr); } else { - return npriv->nfs_packet; + return packet; } } } - - return npriv->nfs_packet; } /* @@ -505,7 +569,13 @@ static int rpc_lookup_req(struct nfs_priv *npriv, uint32_t prog, uint32_t ver) port = ntoh32(net_read_uint32(nfs_packet->data + sizeof(struct rpc_reply))); - free(nfs_packet); + nfs_free_packet(nfs_packet); + + if (port == 0) { + pr_warn("No UDP port for RPC program %i! " + "Is your NFS server TCP only?\n", prog); + return -ENOENT; + } return port; } @@ -645,7 +715,7 @@ static uint32_t *nfs_read_post_op_attr(uint32_t *p, struct inode *inode) static int nfs_mount_req(struct nfs_priv *npriv) { uint32_t data[1024]; - uint32_t *p; + uint32_t *p, status; int len; int pathlen; struct packet *nfs_packet; @@ -670,18 +740,29 @@ static int nfs_mount_req(struct nfs_priv *npriv) if (IS_ERR(nfs_packet)) return PTR_ERR(nfs_packet); - p = (void *)nfs_packet->data + sizeof(struct rpc_reply) + 4; + p = (void *)nfs_packet->data + sizeof(struct rpc_reply); + + /* + * Theoretically the error status is one of MNT3ERR_..., but the NFS + * constants are identical. + */ + status = ntoh32(net_read_uint32(p++)); + if (status != NFS3_OK) { + int ret; + pr_err("Mounting failed: %s\n", nfserrstr(status, &ret)); + return ret; + } npriv->rootfh.size = ntoh32(net_read_uint32(p++)); if (npriv->rootfh.size > NFS3_FHSIZE) { printf("%s: file handle too big: %lu\n", __func__, (unsigned long)npriv->rootfh.size); - free(nfs_packet); + nfs_free_packet(nfs_packet); return -EIO; } memcpy(npriv->rootfh.data, p, npriv->rootfh.size); - free(nfs_packet); + nfs_free_packet(nfs_packet); return 0; } @@ -709,7 +790,7 @@ static void nfs_umount_req(struct nfs_priv *npriv) nfs_packet = rpc_req(npriv, PROG_MOUNT, MOUNT_UMOUNT, data, len); if (!IS_ERR(nfs_packet)) - free(nfs_packet); + nfs_free_packet(nfs_packet); } /* @@ -722,7 +803,7 @@ static int nfs_lookup_req(struct nfs_priv *npriv, struct nfs_fh *fh, { struct nfs_inode *ninode = nfsi(inode); uint32_t data[1024]; - uint32_t *p; + uint32_t *p, status; int len; struct packet *nfs_packet; @@ -764,10 +845,17 @@ static int nfs_lookup_req(struct nfs_priv *npriv, struct nfs_fh *fh, if (IS_ERR(nfs_packet)) return PTR_ERR(nfs_packet); - p = (void *)nfs_packet->data + sizeof(struct rpc_reply) + 4; + p = (void *)nfs_packet->data + sizeof(struct rpc_reply); + status = ntoh32(net_read_uint32(p++)); + if (status != NFS3_OK) { + int ret; + pr_err("Lookup failed: %s\n", nfserrstr(status, &ret)); + return ret; + } ninode->fh.size = ntoh32(net_read_uint32(p++)); if (ninode->fh.size > NFS3_FHSIZE) { + nfs_free_packet(nfs_packet); debug("%s: file handle too big: %u\n", __func__, ninode->fh.size); return -EIO; @@ -777,7 +865,7 @@ static int nfs_lookup_req(struct nfs_priv *npriv, struct nfs_fh *fh, nfs_read_post_op_attr(p, inode); - free(nfs_packet); + nfs_free_packet(nfs_packet); return 0; } @@ -789,7 +877,7 @@ static int nfs_lookup_req(struct nfs_priv *npriv, struct nfs_fh *fh, static void *nfs_readdirattr_req(struct nfs_priv *npriv, struct nfs_dir *dir) { uint32_t data[1024]; - uint32_t *p; + uint32_t *p, status; int len; struct packet *nfs_packet; void *buf; @@ -847,7 +935,13 @@ static void *nfs_readdirattr_req(struct nfs_priv *npriv, struct nfs_dir *dir) if (IS_ERR(nfs_packet)) return NULL; - p = (void *)nfs_packet->data + sizeof(struct rpc_reply) + 4; + p = (void *)nfs_packet->data + sizeof(struct rpc_reply); + status = ntoh32(net_read_uint32(p++)); + if (status != NFS3_OK) { + pr_err("Readdir failed: %s\n", nfserrstr(status, NULL)); + return NULL; + } + p = nfs_read_post_op_attr(p, NULL); /* update cookieverf */ @@ -857,7 +951,7 @@ static void *nfs_readdirattr_req(struct nfs_priv *npriv, struct nfs_dir *dir) len = (void *)nfs_packet->data + nfs_packet->len - (void *)p; if (!len) { printf("%s: huh, no payload left\n", __func__); - free(nfs_packet); + nfs_free_packet(nfs_packet); return NULL; } @@ -865,7 +959,7 @@ static void *nfs_readdirattr_req(struct nfs_priv *npriv, struct nfs_dir *dir) memcpy(buf, p, len); - free(nfs_packet); + nfs_free_packet(nfs_packet); xdr_init(&dir->stream, buf, len); @@ -881,8 +975,8 @@ static int nfs_read_req(struct file_priv *priv, uint64_t offset, uint32_t readlen) { uint32_t data[1024]; - uint32_t *p; - int len; + uint32_t *p, status; + int len, ret; struct packet *nfs_packet; uint32_t rlen, eof; @@ -924,7 +1018,12 @@ static int nfs_read_req(struct file_priv *priv, uint64_t offset, if (IS_ERR(nfs_packet)) return PTR_ERR(nfs_packet); - p = (void *)nfs_packet->data + sizeof(struct rpc_reply) + 4; + p = (void *)nfs_packet->data + sizeof(struct rpc_reply); + status = ntoh32(net_read_uint32(p++)); + if (status != NFS3_OK) { + pr_err("Read failed: %s\n", nfserrstr(status, &ret)); + return ret; + } p = nfs_read_post_op_attr(p, NULL); @@ -942,30 +1041,31 @@ static int nfs_read_req(struct file_priv *priv, uint64_t offset, p += 2; if (readlen && !rlen && !eof) { - free(nfs_packet); + nfs_free_packet(nfs_packet); return -EIO; } kfifo_put(priv->fifo, (char *)p, rlen); - free(nfs_packet); + nfs_free_packet(nfs_packet); return 0; } -static void nfs_handler(void *ctx, char *packet, unsigned len) +static void nfs_handler(void *ctx, char *p, unsigned len) { - char *pkt = net_eth_to_udp_payload(packet); + char *pkt = net_eth_to_udp_payload(p); struct nfs_priv *npriv = ctx; + struct packet *packet; - nfs_state = STATE_DONE; + packet = xmalloc(sizeof(*packet) + len); + memcpy(packet->data, pkt, len); + packet->len = len; - npriv->nfs_packet = xmalloc(sizeof(*npriv->nfs_packet) + len); - memcpy(npriv->nfs_packet->data, pkt, len); - npriv->nfs_packet->len = len; + list_add_tail(&packet->list, &npriv->packets); } -static int nfs_truncate(struct device_d *dev, FILE *f, loff_t size) +static int nfs_truncate(struct device *dev, FILE *f, loff_t size) { return -ENOSYS; } @@ -982,7 +1082,7 @@ static int nfs_readlink_req(struct nfs_priv *npriv, struct nfs_fh *fh, char **target) { uint32_t data[1024]; - uint32_t *p; + uint32_t *p, status; uint32_t len; struct packet *nfs_packet; @@ -1018,13 +1118,19 @@ static int nfs_readlink_req(struct nfs_priv *npriv, struct nfs_fh *fh, if (IS_ERR(nfs_packet)) return PTR_ERR(nfs_packet); - p = (void *)nfs_packet->data + sizeof(struct rpc_reply) + 4; + p = (void *)nfs_packet->data + sizeof(struct rpc_reply); + status = ntoh32(net_read_uint32(p++)); + if (status != NFS3_OK) { + int ret; + pr_err("Readlink failed: %s\n", nfserrstr(status, &ret)); + return ret; + } p = nfs_read_post_op_attr(p, NULL); len = ntoh32(net_read_uint32(p)); /* new path length */ - len = max_t(unsigned int, len, + len = min_t(unsigned int, len, nfs_packet->len - sizeof(struct rpc_reply) - sizeof(uint32_t)); p++; @@ -1032,7 +1138,7 @@ static int nfs_readlink_req(struct nfs_priv *npriv, struct nfs_fh *fh, *target = xzalloc(len + 1); memcpy(*target, p, len); - free(nfs_packet); + nfs_free_packet(nfs_packet); return 0; } @@ -1050,7 +1156,7 @@ static const char *nfs_get_link(struct dentry *dentry, struct inode *inode) return inode->i_link; } -static int nfs_open(struct device_d *dev, FILE *file, const char *filename) +static int nfs_open(struct device *dev, FILE *file, const char *filename) { struct inode *inode = file->f_inode; struct nfs_inode *ninode = nfsi(inode); @@ -1072,7 +1178,7 @@ static int nfs_open(struct device_d *dev, FILE *file, const char *filename) return 0; } -static int nfs_close(struct device_d *dev, FILE *file) +static int nfs_close(struct device *dev, FILE *file) { struct file_priv *priv = file->priv; @@ -1081,13 +1187,13 @@ static int nfs_close(struct device_d *dev, FILE *file) return 0; } -static int nfs_write(struct device_d *_dev, FILE *file, const void *inbuf, - size_t insize) +static int nfs_write(struct device *_dev, FILE *file, const void *inbuf, + size_t insize) { return -ENOSYS; } -static int nfs_read(struct device_d *dev, FILE *file, void *buf, size_t insize) +static int nfs_read(struct device *dev, FILE *file, void *buf, size_t insize) { struct file_priv *priv = file->priv; @@ -1103,7 +1209,7 @@ static int nfs_read(struct device_d *dev, FILE *file, void *buf, size_t insize) return kfifo_get(priv->fifo, buf, insize); } -static int nfs_lseek(struct device_d *dev, FILE *file, loff_t pos) +static int nfs_lseek(struct device *dev, FILE *file, loff_t pos) { struct file_priv *priv = file->priv; @@ -1203,6 +1309,13 @@ static struct inode *nfs_alloc_inode(struct super_block *sb) return &node->inode; } +static void nfs_destroy_inode(struct inode *inode) +{ + struct nfs_inode *node = nfsi(inode); + + free(node); +} + static const struct inode_operations nfs_file_inode_operations; static const struct file_operations nfs_dir_operations; static const struct inode_operations nfs_dir_inode_operations; @@ -1274,11 +1387,12 @@ static const struct inode_operations nfs_dir_inode_operations = static const struct super_operations nfs_ops = { .alloc_inode = nfs_alloc_inode, + .destroy_inode = nfs_destroy_inode, }; static char *rootnfsopts; -static void nfs_set_rootarg(struct nfs_priv *npriv, struct fs_device_d *fsdev) +static void nfs_set_rootarg(struct nfs_priv *npriv, struct fs_device *fsdev) { char *str, *tmp; const char *bootargs; @@ -1311,9 +1425,9 @@ static void nfs_set_rootarg(struct nfs_priv *npriv, struct fs_device_d *fsdev) free(str); } -static int nfs_probe(struct device_d *dev) +static int nfs_probe(struct device *dev) { - struct fs_device_d *fsdev = dev_to_fs_device(dev); + struct fs_device *fsdev = dev_to_fs_device(dev); struct nfs_priv *npriv = xzalloc(sizeof(struct nfs_priv)); struct super_block *sb = &fsdev->sb; char *tmp = xstrdup(fsdev->backingstore); @@ -1323,6 +1437,8 @@ static int nfs_probe(struct device_d *dev) dev->priv = npriv; + INIT_LIST_HEAD(&npriv->packets); + debug("nfs: mount: %s\n", fsdev->backingstore); path = strchr(tmp, ':'); @@ -1389,6 +1505,7 @@ static int nfs_probe(struct device_d *dev) free(tmp); sb->s_op = &nfs_ops; + sb->s_d_op = &no_revalidate_d_ops; inode = new_inode(sb); nfs_set_fh(inode, &npriv->rootfh); @@ -1408,7 +1525,7 @@ err: return ret; } -static void nfs_remove(struct device_d *dev) +static void nfs_remove(struct device *dev) { struct nfs_priv *npriv = dev->priv; @@ -1419,7 +1536,7 @@ static void nfs_remove(struct device_d *dev) free(npriv); } -static struct fs_driver_d nfs_driver = { +static struct fs_driver nfs_driver = { .open = nfs_open, .close = nfs_close, .read = nfs_read, |