mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
237 lines
4.9 KiB
237 lines
4.9 KiB
// 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 <[email protected]> |
|
* Copyright (C) 2002 by Ron Minnich <[email protected]> |
|
*/ |
|
|
|
#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) { |
|
spin_lock(&inode->i_lock); |
|
hlist_del(&fid->ilist); |
|
spin_unlock(&inode->i_lock); |
|
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, |
|
};
|
|
|