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