diff --git a/fs/Kconfig b/fs/Kconfig index 195a5cb..4d0e278 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -52,7 +52,6 @@ config FS_NFS depends on NET - select FS_LEGACY bool prompt "nfs support" diff --git a/fs/nfs.c b/fs/nfs.c index 75cd127..eb5db34 100644 --- a/fs/nfs.c +++ b/fs/nfs.c @@ -20,6 +20,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +#define pr_fmt(fmt) "NFS: " fmt #include #include @@ -127,6 +128,11 @@ #define NFS_TIMEOUT (2 * SECOND) #define NFS_MAX_RESEND 5 +struct nfs_fh { + unsigned short size; + unsigned char data[NFS3_FHSIZE]; +}; + struct nfs_priv { struct net_connection *con; IPaddr_t server; @@ -136,18 +142,34 @@ uint16_t nfs_port; unsigned manual_nfs_port:1; uint32_t rpc_id; - uint32_t rootfh_len; - char rootfh[NFS3_FHSIZE]; + struct nfs_fh rootfh; }; struct file_priv { struct kfifo *fifo; void *buf; - uint32_t filefh_len; - char filefh[NFS3_FHSIZE]; + struct nfs_priv *npriv; + struct nfs_fh fh; +}; + +struct nfs_inode { + struct inode inode; + struct nfs_fh fh; struct nfs_priv *npriv; }; +static inline struct nfs_inode *nfsi(struct inode *inode) +{ + return container_of(inode, struct nfs_inode, inode); +} + +static void nfs_set_fh(struct inode *inode, struct nfs_fh *fh) +{ + struct nfs_inode *ninode = nfsi(inode); + + ninode->fh = *fh; +} + static uint64_t nfs_timer_start; static int nfs_state; @@ -234,17 +256,14 @@ #define XDR_QUADLEN(l) (((l) + 3) >> 2) struct nfs_dir { - DIR dir; - /* * stream points to the next entry3 in the reply member of READDIR3res * (if any, to the end indicator otherwise). */ struct xdr_stream stream; - struct dirent ent; - struct file_priv *priv; uint64_t cookie; char cookieverf[NFS3_COOKIEVERFSIZE]; + struct nfs_fh fh; }; static void xdr_init(struct xdr_stream *stream, void *buf, int len) @@ -300,11 +319,11 @@ return 0; out_nametoolong: - printf("%s: returned a too long filename: %u\n", __func__, count); + pr_err("%s: returned a too long filename: %u\n", __func__, count); return -ENAMETOOLONG; out_overflow: - printf("%s: premature end of packet\n", __func__); + pr_err("%s: premature end of packet\n", __func__); return -EIO; } @@ -486,16 +505,16 @@ return p + 2; } -static uint32_t *nfs_add_fh3(uint32_t *p, unsigned fh_len, const char *fh) +static uint32_t *nfs_add_fh3(uint32_t *p, struct nfs_fh *fh) { - *p++ = hton32(fh_len); + *p++ = hton32(fh->size); /* zero padding */ - if (fh_len & 3) - p[fh_len / 4] = 0; + if (fh->size & 3) + p[fh->size / 4] = 0; - memcpy(p, fh, fh_len); - p += DIV_ROUND_UP(fh_len, 4); + memcpy(p, fh->data, fh->size); + p += DIV_ROUND_UP(fh->size, 4); return p; } @@ -532,33 +551,36 @@ { 0x00800, S_ISUID }, }; -static int nfs_fattr3_to_stat(uint32_t *p, struct stat *s) +static int nfs_fattr3_to_stat(uint32_t *p, struct inode *inode) { uint32_t mode; size_t i; + if (!inode) + return 0; + /* offsetof(struct fattr3, type) = 0 */ switch (ntoh32(net_read_uint32(p + 0))) { case NF3REG: - s->st_mode = S_IFREG; + inode->i_mode = S_IFREG; break; case NF3DIR: - s->st_mode = S_IFDIR; + inode->i_mode = S_IFDIR; break; case NF3BLK: - s->st_mode = S_IFBLK; + inode->i_mode = S_IFBLK; break; case NF3CHR: - s->st_mode = S_IFCHR; + inode->i_mode = S_IFCHR; break; case NF3LNK: - s->st_mode = S_IFLNK; + inode->i_mode = S_IFLNK; break; case NF3SOCK: - s->st_mode = S_IFSOCK; + inode->i_mode = S_IFSOCK; break; case NF3FIFO: - s->st_mode = S_IFIFO; + inode->i_mode = S_IFIFO; break; default: printf("%s: invalid mode %x\n", @@ -570,18 +592,17 @@ mode = ntoh32(net_read_uint32(p + 1)); for (i = 0; i < ARRAY_SIZE(nfs3_mode_bits); ++i) { if (mode & nfs3_mode_bits[i].nfsmode) - s->st_mode |= nfs3_mode_bits[i].statmode; + inode->i_mode |= nfs3_mode_bits[i].statmode; } /* offsetof(struct fattr3, size) = 20 */ - s->st_size = ntoh64(net_read_uint64(p + 5)); + inode->i_size = ntoh64(net_read_uint64(p + 5)); return 0; } -static uint32_t *nfs_read_post_op_attr(uint32_t *p, struct stat **s) +static uint32_t *nfs_read_post_op_attr(uint32_t *p, struct inode *inode) { - struct stat dummy; /* * union post_op_attr switch (bool attributes_follow) { * case TRUE: @@ -592,11 +613,8 @@ */ if (ntoh32(net_read_uint32(p++))) { - nfs_fattr3_to_stat(p, s ? *s : &dummy); + nfs_fattr3_to_stat(p, inode); p += 21; - } else if (s) { - /* no attributes available */ - *s = NULL; } return p; @@ -635,14 +653,14 @@ p = nfs_packet + sizeof(struct rpc_reply) + 4; - npriv->rootfh_len = ntoh32(net_read_uint32(p++)); - if (npriv->rootfh_len > NFS3_FHSIZE) { + 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_len); + (unsigned long)npriv->rootfh.size); return -EIO; } - memcpy(npriv->rootfh, p, npriv->rootfh_len); - p += DIV_ROUND_UP(npriv->rootfh_len, 4); + memcpy(npriv->rootfh.data, p, npriv->rootfh.size); + p += DIV_ROUND_UP(npriv->rootfh.size, 4); return 0; } @@ -674,9 +692,10 @@ * * *s is set to NULL if LOOKUP3resok doesn't contain obj_attributes. */ -static int nfs_lookup_req(struct file_priv *priv, - uint32_t filename_len, const char *filename, struct stat **s) +static int nfs_lookup_req(struct nfs_priv *npriv, struct nfs_fh *fh, + const char *filename, struct inode *inode) { + struct nfs_inode *ninode = nfsi(inode); uint32_t data[1024]; uint32_t *p; int len; @@ -709,73 +728,29 @@ p = rpc_add_credentials(p); /* what.dir */ - p = nfs_add_fh3(p, priv->filefh_len, priv->filefh); + p = nfs_add_fh3(p, fh); /* what.name */ - p = nfs_add_filename(p, filename_len, filename); + p = nfs_add_filename(p, strlen(filename), filename); len = p - &(data[0]); - ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_LOOKUP, data, len); + ret = rpc_req(npriv, PROG_NFS, NFSPROC3_LOOKUP, data, len); if (ret) return ret; p = nfs_packet + sizeof(struct rpc_reply) + 4; - priv->filefh_len = ntoh32(net_read_uint32(p++)); - if (priv->filefh_len > NFS3_FHSIZE) { - debug("%s: file handle too big: %lu\n", __func__, - (unsigned long)priv->filefh_len); + ninode->fh.size = ntoh32(net_read_uint32(p++)); + if (ninode->fh.size > NFS3_FHSIZE) { + debug("%s: file handle too big: %u\n", __func__, + ninode->fh.size); return -EIO; } - memcpy(priv->filefh, p, priv->filefh_len); - p += DIV_ROUND_UP(priv->filefh_len, 4); + memcpy(ninode->fh.data, p, ninode->fh.size); + p += DIV_ROUND_UP(ninode->fh.size, 4); - if (s) - nfs_read_post_op_attr(p, s); - - return 0; -} - -static int nfs_attr_req(struct file_priv *priv, struct stat *s) -{ - uint32_t data[1024]; - uint32_t *p; - int len; - int ret; - - /* - * struct GETATTR3args { - * nfs_fh3 object; - * } - * - * struct GETATTR3resok { - * fattr3 obj_attributes; - * }; - * - * union GETATTR3res switch (nfsstat3 status) { - * case NFS3_OK: - * GETATTR3resok resok; - * default: - * void; - * } - */ - - p = &(data[0]); - p = rpc_add_credentials(p); - - /* object */ - p = nfs_add_fh3(p, priv->filefh_len, priv->filefh); - - len = p - &(data[0]); - - ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_GETATTR, data, len); - if (ret) - return ret; - - p = nfs_packet + sizeof(struct rpc_reply) + 4; - - nfs_fattr3_to_stat(p, s); + nfs_read_post_op_attr(p, inode); return 0; } @@ -784,7 +759,7 @@ * returns with dir->stream pointing to the first entry * of dirlist3 res.resok.reply */ -static void *nfs_readdirattr_req(struct file_priv *priv, struct nfs_dir *dir) +static void *nfs_readdirattr_req(struct nfs_priv *npriv, struct nfs_dir *dir) { uint32_t data[1024]; uint32_t *p; @@ -833,7 +808,7 @@ p = &(data[0]); p = rpc_add_credentials(p); - p = nfs_add_fh3(p, priv->filefh_len, priv->filefh); + p = nfs_add_fh3(p, &dir->fh); p = nfs_add_uint64(p, dir->cookie); memcpy(p, dir->cookieverf, NFS3_COOKIEVERFSIZE); @@ -841,7 +816,7 @@ p = nfs_add_uint32(p, 1024); /* count */ - ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_READDIR, data, p - data); + ret = rpc_req(npriv, PROG_NFS, NFSPROC3_READDIR, data, p - data); if (ret) return NULL; @@ -909,7 +884,7 @@ p = &(data[0]); p = rpc_add_credentials(p); - p = nfs_add_fh3(p, priv->filefh_len, priv->filefh); + p = nfs_add_fh3(p, &priv->fh); p = nfs_add_uint64(p, offset); p = nfs_add_uint32(p, readlen); @@ -953,82 +928,11 @@ nfs_len = len; } -static int nfs_create(struct device_d *dev, const char *pathname, mode_t mode) -{ - return -ENOSYS; -} - -static int nfs_unlink(struct device_d *dev, const char *pathname) -{ - return -ENOSYS; -} - -static int nfs_mkdir(struct device_d *dev, const char *pathname) -{ - return -ENOSYS; -} - -static int nfs_rmdir(struct device_d *dev, const char *pathname) -{ - return -ENOSYS; -} - static int nfs_truncate(struct device_d *dev, FILE *f, ulong size) { return -ENOSYS; } -static struct file_priv *nfs_do_open(struct device_d *dev, - const char *filename, struct stat **s) -{ - struct file_priv *priv; - struct nfs_priv *npriv = dev->priv; - int ret; - const char *tok; - - debug("%s: filename = %s\n", __func__, filename); - priv = xzalloc(sizeof(*priv)); - - priv->npriv = npriv; - - if (!*filename) { - priv->filefh_len = npriv->rootfh_len; - memcpy(priv->filefh, npriv->rootfh, npriv->rootfh_len); - return priv; - } - - filename++; - - priv->filefh_len = npriv->rootfh_len; - memcpy(priv->filefh, npriv->rootfh, NFS3_FHSIZE); - - while (*filename) { - size_t flen; - - tok = strchr(filename, '/'); - if (tok) - flen = tok - filename; - else - flen = strlen(filename); - - ret = nfs_lookup_req(priv, flen, filename, s); - if (ret) - goto out; - - if (tok) - filename += flen + 1; - else - break; - } - - return priv; - -out: - free(priv); - - return ERR_PTR(ret); -} - static void nfs_do_close(struct file_priv *priv) { if (priv->fifo) @@ -1037,34 +941,8 @@ free(priv); } -static struct file_priv *nfs_do_stat(struct device_d *dev, - const char *filename, struct stat *s) -{ - struct file_priv *priv; - int ret; - struct stat **sptr = &s; - - debug("%s: filename = %s\n", __func__, filename); - priv = nfs_do_open(dev, filename, sptr); - if (IS_ERR(priv)) - return priv; - - if (!*sptr) { - /* - * The nfs server didn't provide obj_attributes in the lookup - * reply, so ask for them explicitly. - */ - ret = nfs_attr_req(priv, s); - if (ret) { - nfs_do_close(priv); - return ERR_PTR(ret); - } - } - - return priv; -} - -static int nfs_readlink_req(struct file_priv *priv, char* buf, size_t size) +static int nfs_readlink_req(struct nfs_priv *npriv, struct nfs_fh *fh, + char **target) { uint32_t data[1024]; uint32_t *p; @@ -1095,11 +973,11 @@ p = &(data[0]); p = rpc_add_credentials(p); - p = nfs_add_fh3(p, priv->filefh_len, priv->filefh); + p = nfs_add_fh3(p, fh); len = p - &(data[0]); - ret = rpc_req(priv->npriv, PROG_NFS, NFSPROC3_READLINK, data, len); + ret = rpc_req(npriv, PROG_NFS, NFSPROC3_READLINK, data, len); if (ret) return ret; @@ -1110,44 +988,37 @@ len = ntoh32(net_read_uint32(p)); /* new path length */ p++; - if (len > size) - return -ENOMEM; - - memcpy(buf, p, len); + *target = xzalloc(len + 1); + memcpy(*target, p, len); return 0; } -static int nfs_readlink(struct device_d *dev, const char *filename, - char *realname, size_t size) +static const char *nfs_get_link(struct dentry *dentry, struct inode *inode) { - struct file_priv *priv; + struct nfs_inode *ninode = nfsi(inode); + struct nfs_priv *npriv = ninode->npriv; int ret; - priv = nfs_do_open(dev, filename, NULL); - if (IS_ERR(priv)) - return PTR_ERR(priv); + ret = nfs_readlink_req(npriv, &ninode->fh, &inode->i_link); + if (ret) + return ERR_PTR(ret); - ret = nfs_readlink_req(priv, realname, size); - if (ret) { - nfs_do_close(priv); - return ret; - } - - return 0; + return inode->i_link; } static int nfs_open(struct device_d *dev, FILE *file, const char *filename) { + struct inode *inode = file->f_inode; + struct nfs_inode *ninode = nfsi(inode); + struct nfs_priv *npriv = ninode->npriv; struct file_priv *priv; - struct stat s; - priv = nfs_do_stat(dev, filename, &s); - if (IS_ERR(priv)) - return PTR_ERR(priv); - + priv = xzalloc(sizeof(*priv)); + priv->fh = ninode->fh; + priv->npriv = npriv; file->priv = priv; - file->size = s.st_size; + file->size = inode->i_size; priv->fifo = kfifo_alloc(1024); if (!priv->fifo) { @@ -1199,115 +1070,170 @@ return file->pos; } -static DIR *nfs_opendir(struct device_d *dev, const char *pathname) +static int nfs_iterate(struct file *file, struct dir_context *ctx) { - struct file_priv *priv; + struct dentry *dentry = file->f_path.dentry; + struct inode *dir = d_inode(dentry); + struct nfs_priv *npriv = nfsi(dir)->npriv; void *buf = NULL; - struct nfs_dir *dir; - - priv = nfs_do_open(dev, pathname, NULL); - if (IS_ERR(priv)) - return NULL; - - dir = xzalloc(sizeof(*dir)); - dir->priv = priv; - - /* cookie == 0 and cookieverf == 0 means start of dir */ - buf = nfs_readdirattr_req(priv, dir); - if (!buf) { - debug("%s: nfs_readdirattr_req failed\n", __func__); - goto err; - } - - return &dir->dir; - -err: - free(buf); - free(dir); - nfs_do_close(priv); - return NULL; -} - -static struct dirent *nfs_readdir(struct device_d *dev, DIR *dir) -{ - struct nfs_dir *ndir = container_of(dir, struct nfs_dir, dir); - uint32_t *p; + struct nfs_dir *ndir; + struct xdr_stream *xdr; int ret; - int len; - struct xdr_stream *xdr = &ndir->stream; + uint32_t *p, len; -again: - p = xdr_inline_decode(xdr, 4); - if (!p) { - printf("%s: premature end of packet\n", __func__); - return NULL; - } + ndir = xzalloc(sizeof(*ndir)); + ndir->fh = nfsi(dir)->fh; - if (!net_read_uint32(p)) { - /* eof? */ - p = xdr_inline_decode(xdr, 4); - if (!p) { - printf("%s: premature end of packet\n", __func__); - return NULL; - } - if (net_read_uint32(p)) - return NULL; - - if (!nfs_readdirattr_req(ndir->priv, ndir)) { - printf("%s: nfs_readdirattr_req failed\n", __func__); - return NULL; + while (1) { + /* cookie == 0 and cookieverf == 0 means start of dir */ + buf = nfs_readdirattr_req(npriv, ndir); + if (!buf) { + pr_err("%s: nfs_readdirattr_req failed\n", __func__); + ret = -EINVAL; + goto out; } - goto again; + xdr = &ndir->stream; + + while (1) { + char name[256]; + + p = xdr_inline_decode(xdr, 4); + if (!p) + goto err_eop; + + if (!net_read_uint32(p)) { + /* eof? */ + p = xdr_inline_decode(xdr, 4); + if (!p) + goto err_eop; + + if (net_read_uint32(p)) { + ret = 0; + goto out; + } + + break; + } + + /* skip over fileid */ + p = xdr_inline_decode(xdr, 8); + if (!p) + goto err_eop; + + ret = decode_filename(xdr, name, &len); + if (ret) + goto out; + + dir_emit(ctx, name, len, 0, DT_UNKNOWN); + + p = xdr_inline_decode(xdr, 8); + if (!p) + goto err_eop; + + ndir->cookie = ntoh64(net_read_uint64(p)); + } + free(buf); } - /* there is another entry available in the last reply */ + ret = 0; - /* skip over fileid */ - p = xdr_inline_decode(xdr, 8); - if (!p) { - printf("%s: premature end of packet\n", __func__); - return NULL; - } - - ret = decode_filename(xdr, ndir->ent.d_name, &len); - if (ret) - return NULL; - - p = xdr_inline_decode(xdr, 8); - if (!p) { - printf("%s: premature end of packet\n", __func__); - return NULL; - } - ndir->cookie = ntoh64(net_read_uint64(p)); - - return &ndir->ent; -} - -static int nfs_closedir(struct device_d *dev, DIR *dir) -{ - struct nfs_dir *ndir = (void *)dir; - - nfs_do_close(ndir->priv); +out: free(ndir->stream.buf); free(ndir); + return ret; + +err_eop: + pr_err("Unexpected end of packet\n"); + + return -EIO; +} + +static struct inode *nfs_alloc_inode(struct super_block *sb) +{ + struct nfs_inode *node; + + node = xzalloc(sizeof(*node)); + if (!node) + return NULL; + + return &node->inode; +} + +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; +static const struct file_operations nfs_file_operations; +static const struct inode_operations nfs_symlink_inode_operations = { + .get_link = nfs_get_link, +}; + +static int nfs_init_inode(struct nfs_priv *npriv, struct inode *inode, + unsigned int mode) +{ + struct nfs_inode *ninode = nfsi(inode); + + ninode->npriv = npriv; + + inode->i_ino = get_next_ino(); + inode->i_mode = mode; + + switch (inode->i_mode & S_IFMT) { + default: + return -EINVAL; + case S_IFREG: + inode->i_op = &nfs_file_inode_operations; + inode->i_fop = &nfs_file_operations; + break; + case S_IFDIR: + inode->i_op = &nfs_dir_inode_operations; + inode->i_fop = &nfs_dir_operations; + inc_nlink(inode); + break; + case S_IFLNK: + inode->i_op = &nfs_symlink_inode_operations; + break; + } + return 0; } -static int nfs_stat(struct device_d *dev, const char *filename, struct stat *s) +static struct dentry *nfs_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) { - struct file_priv *priv; + struct nfs_inode *ndir = nfsi(dir); + struct inode *inode = new_inode(dir->i_sb); + struct nfs_priv *npriv = ndir->npriv; + int ret; - priv = nfs_do_stat(dev, filename, s); - if (IS_ERR(priv)) { - return PTR_ERR(priv); - } else { - nfs_do_close(priv); - return 0; - } + if (!inode) + return NULL; + + ret = nfs_lookup_req(npriv, &ndir->fh, dentry->name, inode); + if (ret) + return NULL; + + nfs_init_inode(npriv, inode, inode->i_mode); + + d_add(dentry, inode); + + return NULL; } +static const struct file_operations nfs_dir_operations = { + .iterate = nfs_iterate, +}; + +static const struct inode_operations nfs_dir_inode_operations = +{ + .lookup = nfs_lookup, +}; + +static const struct super_operations nfs_ops = { + .alloc_inode = nfs_alloc_inode, +}; + static char *rootnfsopts; static void nfs_set_rootarg(struct nfs_priv *npriv, struct fs_device_d *fsdev) @@ -1347,8 +1273,10 @@ { struct fs_device_d *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); char *path; + struct inode *inode; int ret; dev->priv = npriv; @@ -1414,6 +1342,13 @@ free(tmp); + sb->s_op = &nfs_ops; + + inode = new_inode(sb); + nfs_set_fh(inode, &npriv->rootfh); + nfs_init_inode(npriv, inode, S_IFDIR); + sb->s_root = d_make_root(inode); + return 0; err2: @@ -1443,17 +1378,8 @@ .close = nfs_close, .read = nfs_read, .lseek = nfs_lseek, - .opendir = nfs_opendir, - .readdir = nfs_readdir, - .closedir = nfs_closedir, - .stat = nfs_stat, - .create = nfs_create, - .unlink = nfs_unlink, - .mkdir = nfs_mkdir, - .rmdir = nfs_rmdir, .write = nfs_write, .truncate = nfs_truncate, - .readlink = nfs_readlink, .flags = 0, .drv = { .probe = nfs_probe,