mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-27 19:29:37 +00:00
![Thomas Gleixner](/assets/img/avatar_default.png)
Based on 1 normalized pattern(s): 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 this program is distributed in the hope that it will be useful but without any warranty without even the implied warranty of merchantability or fitness for a particular purpose see the gnu general public license for more details you should have received a copy of the gnu general public license along with this program if not write to free software foundation 51 franklin street fifth floor boston ma 02111 1301 usa extracted by the scancode license scanner the SPDX license identifier GPL-2.0-only has been chosen to replace the boilerplate/reference in 27 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Richard Fontana <rfontana@redhat.com> Reviewed-by: Alexios Zavras <alexios.zavras@intel.com> Reviewed-by: Steve Winslow <swinslow@gmail.com> Reviewed-by: Allison Randal <allison@lohutok.net> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190528170026.981318839@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
233 lines
4.8 KiB
C
233 lines
4.8 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* linux/fs/9p/vfs_dir.c
|
|
*
|
|
* This file contains vfs directory ops for the 9P2000 protocol.
|
|
*
|
|
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/file.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/string.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/inet.h>
|
|
#include <linux/idr.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/uio.h>
|
|
#include <net/9p/9p.h>
|
|
#include <net/9p/client.h>
|
|
|
|
#include "v9fs.h"
|
|
#include "v9fs_vfs.h"
|
|
#include "fid.h"
|
|
|
|
/**
|
|
* struct p9_rdir - readdir accounting
|
|
* @head: start offset of current dirread buffer
|
|
* @tail: end offset of current dirread buffer
|
|
* @buf: dirread buffer
|
|
*
|
|
* private structure for keeping track of readdir
|
|
* allocated on demand
|
|
*/
|
|
|
|
struct p9_rdir {
|
|
int head;
|
|
int tail;
|
|
uint8_t buf[];
|
|
};
|
|
|
|
/**
|
|
* dt_type - return file type
|
|
* @mistat: mistat structure
|
|
*
|
|
*/
|
|
|
|
static inline int dt_type(struct p9_wstat *mistat)
|
|
{
|
|
unsigned long perm = mistat->mode;
|
|
int rettype = DT_REG;
|
|
|
|
if (perm & P9_DMDIR)
|
|
rettype = DT_DIR;
|
|
if (perm & P9_DMSYMLINK)
|
|
rettype = DT_LNK;
|
|
|
|
return rettype;
|
|
}
|
|
|
|
/**
|
|
* v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir
|
|
* @filp: opened file structure
|
|
* @buflen: Length in bytes of buffer to allocate
|
|
*
|
|
*/
|
|
|
|
static struct p9_rdir *v9fs_alloc_rdir_buf(struct file *filp, int buflen)
|
|
{
|
|
struct p9_fid *fid = filp->private_data;
|
|
if (!fid->rdir)
|
|
fid->rdir = kzalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL);
|
|
return fid->rdir;
|
|
}
|
|
|
|
/**
|
|
* v9fs_dir_readdir - iterate through a directory
|
|
* @file: opened file structure
|
|
* @ctx: actor we feed the entries to
|
|
*
|
|
*/
|
|
|
|
static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx)
|
|
{
|
|
bool over;
|
|
struct p9_wstat st;
|
|
int err = 0;
|
|
struct p9_fid *fid;
|
|
int buflen;
|
|
struct p9_rdir *rdir;
|
|
struct kvec kvec;
|
|
|
|
p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
|
|
fid = file->private_data;
|
|
|
|
buflen = fid->clnt->msize - P9_IOHDRSZ;
|
|
|
|
rdir = v9fs_alloc_rdir_buf(file, buflen);
|
|
if (!rdir)
|
|
return -ENOMEM;
|
|
kvec.iov_base = rdir->buf;
|
|
kvec.iov_len = buflen;
|
|
|
|
while (1) {
|
|
if (rdir->tail == rdir->head) {
|
|
struct iov_iter to;
|
|
int n;
|
|
iov_iter_kvec(&to, READ, &kvec, 1, buflen);
|
|
n = p9_client_read(file->private_data, ctx->pos, &to,
|
|
&err);
|
|
if (err)
|
|
return err;
|
|
if (n == 0)
|
|
return 0;
|
|
|
|
rdir->head = 0;
|
|
rdir->tail = n;
|
|
}
|
|
while (rdir->head < rdir->tail) {
|
|
err = p9stat_read(fid->clnt, rdir->buf + rdir->head,
|
|
rdir->tail - rdir->head, &st);
|
|
if (err <= 0) {
|
|
p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
|
|
return -EIO;
|
|
}
|
|
|
|
over = !dir_emit(ctx, st.name, strlen(st.name),
|
|
v9fs_qid2ino(&st.qid), dt_type(&st));
|
|
p9stat_free(&st);
|
|
if (over)
|
|
return 0;
|
|
|
|
rdir->head += err;
|
|
ctx->pos += err;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* v9fs_dir_readdir_dotl - iterate through a directory
|
|
* @file: opened file structure
|
|
* @ctx: actor we feed the entries to
|
|
*
|
|
*/
|
|
static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx)
|
|
{
|
|
int err = 0;
|
|
struct p9_fid *fid;
|
|
int buflen;
|
|
struct p9_rdir *rdir;
|
|
struct p9_dirent curdirent;
|
|
|
|
p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
|
|
fid = file->private_data;
|
|
|
|
buflen = fid->clnt->msize - P9_READDIRHDRSZ;
|
|
|
|
rdir = v9fs_alloc_rdir_buf(file, buflen);
|
|
if (!rdir)
|
|
return -ENOMEM;
|
|
|
|
while (1) {
|
|
if (rdir->tail == rdir->head) {
|
|
err = p9_client_readdir(fid, rdir->buf, buflen,
|
|
ctx->pos);
|
|
if (err <= 0)
|
|
return err;
|
|
|
|
rdir->head = 0;
|
|
rdir->tail = err;
|
|
}
|
|
|
|
while (rdir->head < rdir->tail) {
|
|
|
|
err = p9dirent_read(fid->clnt, rdir->buf + rdir->head,
|
|
rdir->tail - rdir->head,
|
|
&curdirent);
|
|
if (err < 0) {
|
|
p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
|
|
return -EIO;
|
|
}
|
|
|
|
if (!dir_emit(ctx, curdirent.d_name,
|
|
strlen(curdirent.d_name),
|
|
v9fs_qid2ino(&curdirent.qid),
|
|
curdirent.d_type))
|
|
return 0;
|
|
|
|
ctx->pos = curdirent.d_off;
|
|
rdir->head += err;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* v9fs_dir_release - close a directory
|
|
* @inode: inode of the directory
|
|
* @filp: file pointer to a directory
|
|
*
|
|
*/
|
|
|
|
int v9fs_dir_release(struct inode *inode, struct file *filp)
|
|
{
|
|
struct p9_fid *fid;
|
|
|
|
fid = filp->private_data;
|
|
p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n",
|
|
inode, filp, fid ? fid->fid : -1);
|
|
if (fid)
|
|
p9_client_clunk(fid);
|
|
return 0;
|
|
}
|
|
|
|
const struct file_operations v9fs_dir_operations = {
|
|
.read = generic_read_dir,
|
|
.llseek = generic_file_llseek,
|
|
.iterate_shared = v9fs_dir_readdir,
|
|
.open = v9fs_file_open,
|
|
.release = v9fs_dir_release,
|
|
};
|
|
|
|
const struct file_operations v9fs_dir_operations_dotl = {
|
|
.read = generic_read_dir,
|
|
.llseek = generic_file_llseek,
|
|
.iterate_shared = v9fs_dir_readdir_dotl,
|
|
.open = v9fs_file_open,
|
|
.release = v9fs_dir_release,
|
|
.fsync = v9fs_file_fsync_dotl,
|
|
};
|