162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2017 Omnibond Systems, L.L.C.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include "protocol.h"
762306a36Sopenharmony_ci#include "orangefs-kernel.h"
862306a36Sopenharmony_ci#include "orangefs-bufmap.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cistruct orangefs_dir_part {
1162306a36Sopenharmony_ci	struct orangefs_dir_part *next;
1262306a36Sopenharmony_ci	size_t len;
1362306a36Sopenharmony_ci};
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistruct orangefs_dir {
1662306a36Sopenharmony_ci	__u64 token;
1762306a36Sopenharmony_ci	struct orangefs_dir_part *part;
1862306a36Sopenharmony_ci	loff_t end;
1962306a36Sopenharmony_ci	int error;
2062306a36Sopenharmony_ci};
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define PART_SHIFT (24)
2362306a36Sopenharmony_ci#define PART_SIZE (1<<24)
2462306a36Sopenharmony_ci#define PART_MASK (~(PART_SIZE - 1))
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci * There can be up to 512 directory entries.  Each entry is encoded as
2862306a36Sopenharmony_ci * follows:
2962306a36Sopenharmony_ci * 4 bytes: string size (n)
3062306a36Sopenharmony_ci * n bytes: string
3162306a36Sopenharmony_ci * 1 byte: trailing zero
3262306a36Sopenharmony_ci * padding to 8 bytes
3362306a36Sopenharmony_ci * 16 bytes: khandle
3462306a36Sopenharmony_ci * padding to 8 bytes
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci * The trailer_buf starts with a struct orangefs_readdir_response_s
3762306a36Sopenharmony_ci * which must be skipped to get to the directory data.
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci * The data which is received from the userspace daemon is termed a
4062306a36Sopenharmony_ci * part and is stored in a linked list in case more than one part is
4162306a36Sopenharmony_ci * needed for a large directory.
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci * The position pointer (ctx->pos) encodes the part and offset on which
4462306a36Sopenharmony_ci * to begin reading at.  Bits above PART_SHIFT encode the part and bits
4562306a36Sopenharmony_ci * below PART_SHIFT encode the offset.  Parts are stored in a linked
4662306a36Sopenharmony_ci * list which grows as data is received from the server.  The overhead
4762306a36Sopenharmony_ci * associated with managing the list is presumed to be small compared to
4862306a36Sopenharmony_ci * the overhead of communicating with the server.
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci * As data is received from the server, it is placed at the end of the
5162306a36Sopenharmony_ci * part list.  Data is parsed from the current position as it is needed.
5262306a36Sopenharmony_ci * When data is determined to be corrupt, it is either because the
5362306a36Sopenharmony_ci * userspace component has sent back corrupt data or because the file
5462306a36Sopenharmony_ci * pointer has been moved to an invalid location.  Since the two cannot
5562306a36Sopenharmony_ci * be differentiated, return EIO.
5662306a36Sopenharmony_ci *
5762306a36Sopenharmony_ci * Part zero is synthesized to contains `.' and `..'.  Part one is the
5862306a36Sopenharmony_ci * first part of the part list.
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic int do_readdir(struct orangefs_inode_s *oi,
6262306a36Sopenharmony_ci    struct orangefs_dir *od, struct dentry *dentry,
6362306a36Sopenharmony_ci    struct orangefs_kernel_op_s *op)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct orangefs_readdir_response_s *resp;
6662306a36Sopenharmony_ci	int bufi, r;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/*
6962306a36Sopenharmony_ci	 * Despite the badly named field, readdir does not use shared
7062306a36Sopenharmony_ci	 * memory.  However, there are a limited number of readdir
7162306a36Sopenharmony_ci	 * slots, which must be allocated here.  This flag simply tells
7262306a36Sopenharmony_ci	 * the op scheduler to return the op here for retry.
7362306a36Sopenharmony_ci	 */
7462306a36Sopenharmony_ci	op->uses_shared_memory = 1;
7562306a36Sopenharmony_ci	op->upcall.req.readdir.refn = oi->refn;
7662306a36Sopenharmony_ci	op->upcall.req.readdir.token = od->token;
7762306a36Sopenharmony_ci	op->upcall.req.readdir.max_dirent_count =
7862306a36Sopenharmony_ci	    ORANGEFS_MAX_DIRENT_COUNT_READDIR;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ciagain:
8162306a36Sopenharmony_ci	bufi = orangefs_readdir_index_get();
8262306a36Sopenharmony_ci	if (bufi < 0) {
8362306a36Sopenharmony_ci		od->error = bufi;
8462306a36Sopenharmony_ci		return bufi;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	op->upcall.req.readdir.buf_index = bufi;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	r = service_operation(op, "orangefs_readdir",
9062306a36Sopenharmony_ci	    get_interruptible_flag(dentry->d_inode));
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	orangefs_readdir_index_put(bufi);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (op_state_purged(op)) {
9562306a36Sopenharmony_ci		if (r == -EAGAIN) {
9662306a36Sopenharmony_ci			vfree(op->downcall.trailer_buf);
9762306a36Sopenharmony_ci			goto again;
9862306a36Sopenharmony_ci		} else if (r == -EIO) {
9962306a36Sopenharmony_ci			vfree(op->downcall.trailer_buf);
10062306a36Sopenharmony_ci			od->error = r;
10162306a36Sopenharmony_ci			return r;
10262306a36Sopenharmony_ci		}
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if (r < 0) {
10662306a36Sopenharmony_ci		vfree(op->downcall.trailer_buf);
10762306a36Sopenharmony_ci		od->error = r;
10862306a36Sopenharmony_ci		return r;
10962306a36Sopenharmony_ci	} else if (op->downcall.status) {
11062306a36Sopenharmony_ci		vfree(op->downcall.trailer_buf);
11162306a36Sopenharmony_ci		od->error = op->downcall.status;
11262306a36Sopenharmony_ci		return op->downcall.status;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/*
11662306a36Sopenharmony_ci	 * The maximum size is size per entry times the 512 entries plus
11762306a36Sopenharmony_ci	 * the header.  This is well under the limit.
11862306a36Sopenharmony_ci	 */
11962306a36Sopenharmony_ci	if (op->downcall.trailer_size > PART_SIZE) {
12062306a36Sopenharmony_ci		vfree(op->downcall.trailer_buf);
12162306a36Sopenharmony_ci		od->error = -EIO;
12262306a36Sopenharmony_ci		return -EIO;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	resp = (struct orangefs_readdir_response_s *)
12662306a36Sopenharmony_ci	    op->downcall.trailer_buf;
12762306a36Sopenharmony_ci	od->token = resp->token;
12862306a36Sopenharmony_ci	return 0;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic int parse_readdir(struct orangefs_dir *od,
13262306a36Sopenharmony_ci    struct orangefs_kernel_op_s *op)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct orangefs_dir_part *part, *new;
13562306a36Sopenharmony_ci	size_t count;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	count = 1;
13862306a36Sopenharmony_ci	part = od->part;
13962306a36Sopenharmony_ci	while (part) {
14062306a36Sopenharmony_ci		count++;
14162306a36Sopenharmony_ci		if (part->next)
14262306a36Sopenharmony_ci			part = part->next;
14362306a36Sopenharmony_ci		else
14462306a36Sopenharmony_ci			break;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	new = (void *)op->downcall.trailer_buf;
14862306a36Sopenharmony_ci	new->next = NULL;
14962306a36Sopenharmony_ci	new->len = op->downcall.trailer_size -
15062306a36Sopenharmony_ci	    sizeof(struct orangefs_readdir_response_s);
15162306a36Sopenharmony_ci	if (!od->part)
15262306a36Sopenharmony_ci		od->part = new;
15362306a36Sopenharmony_ci	else
15462306a36Sopenharmony_ci		part->next = new;
15562306a36Sopenharmony_ci	count++;
15662306a36Sopenharmony_ci	od->end = count << PART_SHIFT;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return 0;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic int orangefs_dir_more(struct orangefs_inode_s *oi,
16262306a36Sopenharmony_ci    struct orangefs_dir *od, struct dentry *dentry)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct orangefs_kernel_op_s *op;
16562306a36Sopenharmony_ci	int r;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	op = op_alloc(ORANGEFS_VFS_OP_READDIR);
16862306a36Sopenharmony_ci	if (!op) {
16962306a36Sopenharmony_ci		od->error = -ENOMEM;
17062306a36Sopenharmony_ci		return -ENOMEM;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci	r = do_readdir(oi, od, dentry, op);
17362306a36Sopenharmony_ci	if (r) {
17462306a36Sopenharmony_ci		od->error = r;
17562306a36Sopenharmony_ci		goto out;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci	r = parse_readdir(od, op);
17862306a36Sopenharmony_ci	if (r) {
17962306a36Sopenharmony_ci		od->error = r;
18062306a36Sopenharmony_ci		goto out;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	od->error = 0;
18462306a36Sopenharmony_ciout:
18562306a36Sopenharmony_ci	op_release(op);
18662306a36Sopenharmony_ci	return od->error;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic int fill_from_part(struct orangefs_dir_part *part,
19062306a36Sopenharmony_ci    struct dir_context *ctx)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	const int offset = sizeof(struct orangefs_readdir_response_s);
19362306a36Sopenharmony_ci	struct orangefs_khandle *khandle;
19462306a36Sopenharmony_ci	__u32 *len, padlen;
19562306a36Sopenharmony_ci	loff_t i;
19662306a36Sopenharmony_ci	char *s;
19762306a36Sopenharmony_ci	i = ctx->pos & ~PART_MASK;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	/* The file offset from userspace is too large. */
20062306a36Sopenharmony_ci	if (i > part->len)
20162306a36Sopenharmony_ci		return 1;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/*
20462306a36Sopenharmony_ci	 * If the seek pointer is positioned just before an entry it
20562306a36Sopenharmony_ci	 * should find the next entry.
20662306a36Sopenharmony_ci	 */
20762306a36Sopenharmony_ci	if (i % 8)
20862306a36Sopenharmony_ci		i = i + (8 - i%8)%8;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	while (i < part->len) {
21162306a36Sopenharmony_ci		if (part->len < i + sizeof *len)
21262306a36Sopenharmony_ci			break;
21362306a36Sopenharmony_ci		len = (void *)part + offset + i;
21462306a36Sopenharmony_ci		/*
21562306a36Sopenharmony_ci		 * len is the size of the string itself.  padlen is the
21662306a36Sopenharmony_ci		 * total size of the encoded string.
21762306a36Sopenharmony_ci		 */
21862306a36Sopenharmony_ci		padlen = (sizeof *len + *len + 1) +
21962306a36Sopenharmony_ci		    (8 - (sizeof *len + *len + 1)%8)%8;
22062306a36Sopenharmony_ci		if (part->len < i + padlen + sizeof *khandle)
22162306a36Sopenharmony_ci			goto next;
22262306a36Sopenharmony_ci		s = (void *)part + offset + i + sizeof *len;
22362306a36Sopenharmony_ci		if (s[*len] != 0)
22462306a36Sopenharmony_ci			goto next;
22562306a36Sopenharmony_ci		khandle = (void *)part + offset + i + padlen;
22662306a36Sopenharmony_ci		if (!dir_emit(ctx, s, *len,
22762306a36Sopenharmony_ci		    orangefs_khandle_to_ino(khandle),
22862306a36Sopenharmony_ci		    DT_UNKNOWN))
22962306a36Sopenharmony_ci			return 0;
23062306a36Sopenharmony_ci		i += padlen + sizeof *khandle;
23162306a36Sopenharmony_ci		i = i + (8 - i%8)%8;
23262306a36Sopenharmony_ci		BUG_ON(i > part->len);
23362306a36Sopenharmony_ci		ctx->pos = (ctx->pos & PART_MASK) | i;
23462306a36Sopenharmony_ci		continue;
23562306a36Sopenharmony_cinext:
23662306a36Sopenharmony_ci		i += 8;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci	return 1;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic int orangefs_dir_fill(struct orangefs_inode_s *oi,
24262306a36Sopenharmony_ci    struct orangefs_dir *od, struct dentry *dentry,
24362306a36Sopenharmony_ci    struct dir_context *ctx)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	struct orangefs_dir_part *part;
24662306a36Sopenharmony_ci	size_t count;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	count = ((ctx->pos & PART_MASK) >> PART_SHIFT) - 1;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	part = od->part;
25162306a36Sopenharmony_ci	while (part->next && count) {
25262306a36Sopenharmony_ci		count--;
25362306a36Sopenharmony_ci		part = part->next;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci	/* This means the userspace file offset is invalid. */
25662306a36Sopenharmony_ci	if (count) {
25762306a36Sopenharmony_ci		od->error = -EIO;
25862306a36Sopenharmony_ci		return -EIO;
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	while (part && part->len) {
26262306a36Sopenharmony_ci		int r;
26362306a36Sopenharmony_ci		r = fill_from_part(part, ctx);
26462306a36Sopenharmony_ci		if (r < 0) {
26562306a36Sopenharmony_ci			od->error = r;
26662306a36Sopenharmony_ci			return r;
26762306a36Sopenharmony_ci		} else if (r == 0) {
26862306a36Sopenharmony_ci			/* Userspace buffer is full. */
26962306a36Sopenharmony_ci			break;
27062306a36Sopenharmony_ci		} else {
27162306a36Sopenharmony_ci			/*
27262306a36Sopenharmony_ci			 * The part ran out of data.  Move to the next
27362306a36Sopenharmony_ci			 * part. */
27462306a36Sopenharmony_ci			ctx->pos = (ctx->pos & PART_MASK) +
27562306a36Sopenharmony_ci			    (1 << PART_SHIFT);
27662306a36Sopenharmony_ci			part = part->next;
27762306a36Sopenharmony_ci		}
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci	return 0;
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic loff_t orangefs_dir_llseek(struct file *file, loff_t offset,
28362306a36Sopenharmony_ci    int whence)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	struct orangefs_dir *od = file->private_data;
28662306a36Sopenharmony_ci	/*
28762306a36Sopenharmony_ci	 * Delete the stored data so userspace sees new directory
28862306a36Sopenharmony_ci	 * entries.
28962306a36Sopenharmony_ci	 */
29062306a36Sopenharmony_ci	if (!whence && offset < od->end) {
29162306a36Sopenharmony_ci		struct orangefs_dir_part *part = od->part;
29262306a36Sopenharmony_ci		while (part) {
29362306a36Sopenharmony_ci			struct orangefs_dir_part *next = part->next;
29462306a36Sopenharmony_ci			vfree(part);
29562306a36Sopenharmony_ci			part = next;
29662306a36Sopenharmony_ci		}
29762306a36Sopenharmony_ci		od->token = ORANGEFS_ITERATE_START;
29862306a36Sopenharmony_ci		od->part = NULL;
29962306a36Sopenharmony_ci		od->end = 1 << PART_SHIFT;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci	return default_llseek(file, offset, whence);
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic int orangefs_dir_iterate(struct file *file,
30562306a36Sopenharmony_ci    struct dir_context *ctx)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct orangefs_inode_s *oi;
30862306a36Sopenharmony_ci	struct orangefs_dir *od;
30962306a36Sopenharmony_ci	struct dentry *dentry;
31062306a36Sopenharmony_ci	int r;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	dentry = file->f_path.dentry;
31362306a36Sopenharmony_ci	oi = ORANGEFS_I(dentry->d_inode);
31462306a36Sopenharmony_ci	od = file->private_data;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (od->error)
31762306a36Sopenharmony_ci		return od->error;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (ctx->pos == 0) {
32062306a36Sopenharmony_ci		if (!dir_emit_dot(file, ctx))
32162306a36Sopenharmony_ci			return 0;
32262306a36Sopenharmony_ci		ctx->pos++;
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci	if (ctx->pos == 1) {
32562306a36Sopenharmony_ci		if (!dir_emit_dotdot(file, ctx))
32662306a36Sopenharmony_ci			return 0;
32762306a36Sopenharmony_ci		ctx->pos = 1 << PART_SHIFT;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/*
33162306a36Sopenharmony_ci	 * The seek position is in the first synthesized part but is not
33262306a36Sopenharmony_ci	 * valid.
33362306a36Sopenharmony_ci	 */
33462306a36Sopenharmony_ci	if ((ctx->pos & PART_MASK) == 0)
33562306a36Sopenharmony_ci		return -EIO;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	r = 0;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	/*
34062306a36Sopenharmony_ci	 * Must read more if the user has sought past what has been read
34162306a36Sopenharmony_ci	 * so far.  Stop a user who has sought past the end.
34262306a36Sopenharmony_ci	 */
34362306a36Sopenharmony_ci	while (od->token != ORANGEFS_ITERATE_END &&
34462306a36Sopenharmony_ci	    ctx->pos > od->end) {
34562306a36Sopenharmony_ci		r = orangefs_dir_more(oi, od, dentry);
34662306a36Sopenharmony_ci		if (r)
34762306a36Sopenharmony_ci			return r;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci	if (od->token == ORANGEFS_ITERATE_END && ctx->pos > od->end)
35062306a36Sopenharmony_ci		return -EIO;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	/* Then try to fill if there's any left in the buffer. */
35362306a36Sopenharmony_ci	if (ctx->pos < od->end) {
35462306a36Sopenharmony_ci		r = orangefs_dir_fill(oi, od, dentry, ctx);
35562306a36Sopenharmony_ci		if (r)
35662306a36Sopenharmony_ci			return r;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	/* Finally get some more and try to fill. */
36062306a36Sopenharmony_ci	if (od->token != ORANGEFS_ITERATE_END) {
36162306a36Sopenharmony_ci		r = orangefs_dir_more(oi, od, dentry);
36262306a36Sopenharmony_ci		if (r)
36362306a36Sopenharmony_ci			return r;
36462306a36Sopenharmony_ci		r = orangefs_dir_fill(oi, od, dentry, ctx);
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	return r;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic int orangefs_dir_open(struct inode *inode, struct file *file)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct orangefs_dir *od;
37362306a36Sopenharmony_ci	file->private_data = kmalloc(sizeof(struct orangefs_dir),
37462306a36Sopenharmony_ci	    GFP_KERNEL);
37562306a36Sopenharmony_ci	if (!file->private_data)
37662306a36Sopenharmony_ci		return -ENOMEM;
37762306a36Sopenharmony_ci	od = file->private_data;
37862306a36Sopenharmony_ci	od->token = ORANGEFS_ITERATE_START;
37962306a36Sopenharmony_ci	od->part = NULL;
38062306a36Sopenharmony_ci	od->end = 1 << PART_SHIFT;
38162306a36Sopenharmony_ci	od->error = 0;
38262306a36Sopenharmony_ci	return 0;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic int orangefs_dir_release(struct inode *inode, struct file *file)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct orangefs_dir *od = file->private_data;
38862306a36Sopenharmony_ci	struct orangefs_dir_part *part = od->part;
38962306a36Sopenharmony_ci	while (part) {
39062306a36Sopenharmony_ci		struct orangefs_dir_part *next = part->next;
39162306a36Sopenharmony_ci		vfree(part);
39262306a36Sopenharmony_ci		part = next;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci	kfree(od);
39562306a36Sopenharmony_ci	return 0;
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ciconst struct file_operations orangefs_dir_operations = {
39962306a36Sopenharmony_ci	.llseek = orangefs_dir_llseek,
40062306a36Sopenharmony_ci	.read = generic_read_dir,
40162306a36Sopenharmony_ci	.iterate_shared = orangefs_dir_iterate,
40262306a36Sopenharmony_ci	.open = orangefs_dir_open,
40362306a36Sopenharmony_ci	.release = orangefs_dir_release
40462306a36Sopenharmony_ci};
405