linux/fs/9p/vfs_dir.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * linux/fs/9p/vfs_dir.c
   4 *
   5 * This file contains vfs directory ops for the 9P2000 protocol.
   6 *
   7 *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
   8 *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/errno.h>
  13#include <linux/fs.h>
  14#include <linux/file.h>
  15#include <linux/stat.h>
  16#include <linux/string.h>
  17#include <linux/sched.h>
  18#include <linux/inet.h>
  19#include <linux/idr.h>
  20#include <linux/slab.h>
  21#include <linux/uio.h>
  22#include <net/9p/9p.h>
  23#include <net/9p/client.h>
  24
  25#include "v9fs.h"
  26#include "v9fs_vfs.h"
  27#include "fid.h"
  28
  29/**
  30 * struct p9_rdir - readdir accounting
  31 * @head: start offset of current dirread buffer
  32 * @tail: end offset of current dirread buffer
  33 * @buf: dirread buffer
  34 *
  35 * private structure for keeping track of readdir
  36 * allocated on demand
  37 */
  38
  39struct p9_rdir {
  40        int head;
  41        int tail;
  42        uint8_t buf[];
  43};
  44
  45/**
  46 * dt_type - return file type
  47 * @mistat: mistat structure
  48 *
  49 */
  50
  51static inline int dt_type(struct p9_wstat *mistat)
  52{
  53        unsigned long perm = mistat->mode;
  54        int rettype = DT_REG;
  55
  56        if (perm & P9_DMDIR)
  57                rettype = DT_DIR;
  58        if (perm & P9_DMSYMLINK)
  59                rettype = DT_LNK;
  60
  61        return rettype;
  62}
  63
  64/**
  65 * v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir
  66 * @filp: opened file structure
  67 * @buflen: Length in bytes of buffer to allocate
  68 *
  69 */
  70
  71static struct p9_rdir *v9fs_alloc_rdir_buf(struct file *filp, int buflen)
  72{
  73        struct p9_fid *fid = filp->private_data;
  74        if (!fid->rdir)
  75                fid->rdir = kzalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL);
  76        return fid->rdir;
  77}
  78
  79/**
  80 * v9fs_dir_readdir - iterate through a directory
  81 * @file: opened file structure
  82 * @ctx: actor we feed the entries to
  83 *
  84 */
  85
  86static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx)
  87{
  88        bool over;
  89        struct p9_wstat st;
  90        int err = 0;
  91        struct p9_fid *fid;
  92        int buflen;
  93        struct p9_rdir *rdir;
  94        struct kvec kvec;
  95
  96        p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
  97        fid = file->private_data;
  98
  99        buflen = fid->clnt->msize - P9_IOHDRSZ;
 100
 101        rdir = v9fs_alloc_rdir_buf(file, buflen);
 102        if (!rdir)
 103                return -ENOMEM;
 104        kvec.iov_base = rdir->buf;
 105        kvec.iov_len = buflen;
 106
 107        while (1) {
 108                if (rdir->tail == rdir->head) {
 109                        struct iov_iter to;
 110                        int n;
 111                        iov_iter_kvec(&to, READ, &kvec, 1, buflen);
 112                        n = p9_client_read(file->private_data, ctx->pos, &to,
 113                                           &err);
 114                        if (err)
 115                                return err;
 116                        if (n == 0)
 117                                return 0;
 118
 119                        rdir->head = 0;
 120                        rdir->tail = n;
 121                }
 122                while (rdir->head < rdir->tail) {
 123                        err = p9stat_read(fid->clnt, rdir->buf + rdir->head,
 124                                          rdir->tail - rdir->head, &st);
 125                        if (err <= 0) {
 126                                p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
 127                                return -EIO;
 128                        }
 129
 130                        over = !dir_emit(ctx, st.name, strlen(st.name),
 131                                         v9fs_qid2ino(&st.qid), dt_type(&st));
 132                        p9stat_free(&st);
 133                        if (over)
 134                                return 0;
 135
 136                        rdir->head += err;
 137                        ctx->pos += err;
 138                }
 139        }
 140}
 141
 142/**
 143 * v9fs_dir_readdir_dotl - iterate through a directory
 144 * @file: opened file structure
 145 * @ctx: actor we feed the entries to
 146 *
 147 */
 148static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx)
 149{
 150        int err = 0;
 151        struct p9_fid *fid;
 152        int buflen;
 153        struct p9_rdir *rdir;
 154        struct p9_dirent curdirent;
 155
 156        p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
 157        fid = file->private_data;
 158
 159        buflen = fid->clnt->msize - P9_READDIRHDRSZ;
 160
 161        rdir = v9fs_alloc_rdir_buf(file, buflen);
 162        if (!rdir)
 163                return -ENOMEM;
 164
 165        while (1) {
 166                if (rdir->tail == rdir->head) {
 167                        err = p9_client_readdir(fid, rdir->buf, buflen,
 168                                                ctx->pos);
 169                        if (err <= 0)
 170                                return err;
 171
 172                        rdir->head = 0;
 173                        rdir->tail = err;
 174                }
 175
 176                while (rdir->head < rdir->tail) {
 177
 178                        err = p9dirent_read(fid->clnt, rdir->buf + rdir->head,
 179                                            rdir->tail - rdir->head,
 180                                            &curdirent);
 181                        if (err < 0) {
 182                                p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
 183                                return -EIO;
 184                        }
 185
 186                        if (!dir_emit(ctx, curdirent.d_name,
 187                                      strlen(curdirent.d_name),
 188                                      v9fs_qid2ino(&curdirent.qid),
 189                                      curdirent.d_type))
 190                                return 0;
 191
 192                        ctx->pos = curdirent.d_off;
 193                        rdir->head += err;
 194                }
 195        }
 196}
 197
 198
 199/**
 200 * v9fs_dir_release - close a directory
 201 * @inode: inode of the directory
 202 * @filp: file pointer to a directory
 203 *
 204 */
 205
 206int v9fs_dir_release(struct inode *inode, struct file *filp)
 207{
 208        struct p9_fid *fid;
 209
 210        fid = filp->private_data;
 211        p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n",
 212                 inode, filp, fid ? fid->fid : -1);
 213        if (fid)
 214                p9_client_clunk(fid);
 215        return 0;
 216}
 217
 218const struct file_operations v9fs_dir_operations = {
 219        .read = generic_read_dir,
 220        .llseek = generic_file_llseek,
 221        .iterate_shared = v9fs_dir_readdir,
 222        .open = v9fs_file_open,
 223        .release = v9fs_dir_release,
 224};
 225
 226const struct file_operations v9fs_dir_operations_dotl = {
 227        .read = generic_read_dir,
 228        .llseek = generic_file_llseek,
 229        .iterate_shared = v9fs_dir_readdir_dotl,
 230        .open = v9fs_file_open,
 231        .release = v9fs_dir_release,
 232        .fsync = v9fs_file_fsync_dotl,
 233};
 234