123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright 2017 Omnibond Systems, L.L.C.
- */
- #include "protocol.h"
- #include "orangefs-kernel.h"
- #include "orangefs-bufmap.h"
- struct orangefs_dir_part {
- struct orangefs_dir_part *next;
- size_t len;
- };
- struct orangefs_dir {
- __u64 token;
- struct orangefs_dir_part *part;
- loff_t end;
- int error;
- };
- #define PART_SHIFT (24)
- #define PART_SIZE (1<<24)
- #define PART_MASK (~(PART_SIZE - 1))
- /*
- * There can be up to 512 directory entries. Each entry is encoded as
- * follows:
- * 4 bytes: string size (n)
- * n bytes: string
- * 1 byte: trailing zero
- * padding to 8 bytes
- * 16 bytes: khandle
- * padding to 8 bytes
- *
- * The trailer_buf starts with a struct orangefs_readdir_response_s
- * which must be skipped to get to the directory data.
- *
- * The data which is received from the userspace daemon is termed a
- * part and is stored in a linked list in case more than one part is
- * needed for a large directory.
- *
- * The position pointer (ctx->pos) encodes the part and offset on which
- * to begin reading at. Bits above PART_SHIFT encode the part and bits
- * below PART_SHIFT encode the offset. Parts are stored in a linked
- * list which grows as data is received from the server. The overhead
- * associated with managing the list is presumed to be small compared to
- * the overhead of communicating with the server.
- *
- * As data is received from the server, it is placed at the end of the
- * part list. Data is parsed from the current position as it is needed.
- * When data is determined to be corrupt, it is either because the
- * userspace component has sent back corrupt data or because the file
- * pointer has been moved to an invalid location. Since the two cannot
- * be differentiated, return EIO.
- *
- * Part zero is synthesized to contains `.' and `..'. Part one is the
- * first part of the part list.
- */
- static int do_readdir(struct orangefs_inode_s *oi,
- struct orangefs_dir *od, struct dentry *dentry,
- struct orangefs_kernel_op_s *op)
- {
- struct orangefs_readdir_response_s *resp;
- int bufi, r;
- /*
- * Despite the badly named field, readdir does not use shared
- * memory. However, there are a limited number of readdir
- * slots, which must be allocated here. This flag simply tells
- * the op scheduler to return the op here for retry.
- */
- op->uses_shared_memory = 1;
- op->upcall.req.readdir.refn = oi->refn;
- op->upcall.req.readdir.token = od->token;
- op->upcall.req.readdir.max_dirent_count =
- ORANGEFS_MAX_DIRENT_COUNT_READDIR;
- again:
- bufi = orangefs_readdir_index_get();
- if (bufi < 0) {
- od->error = bufi;
- return bufi;
- }
- op->upcall.req.readdir.buf_index = bufi;
- r = service_operation(op, "orangefs_readdir",
- get_interruptible_flag(dentry->d_inode));
- orangefs_readdir_index_put(bufi);
- if (op_state_purged(op)) {
- if (r == -EAGAIN) {
- vfree(op->downcall.trailer_buf);
- goto again;
- } else if (r == -EIO) {
- vfree(op->downcall.trailer_buf);
- od->error = r;
- return r;
- }
- }
- if (r < 0) {
- vfree(op->downcall.trailer_buf);
- od->error = r;
- return r;
- } else if (op->downcall.status) {
- vfree(op->downcall.trailer_buf);
- od->error = op->downcall.status;
- return op->downcall.status;
- }
- /*
- * The maximum size is size per entry times the 512 entries plus
- * the header. This is well under the limit.
- */
- if (op->downcall.trailer_size > PART_SIZE) {
- vfree(op->downcall.trailer_buf);
- od->error = -EIO;
- return -EIO;
- }
- resp = (struct orangefs_readdir_response_s *)
- op->downcall.trailer_buf;
- od->token = resp->token;
- return 0;
- }
- static int parse_readdir(struct orangefs_dir *od,
- struct orangefs_kernel_op_s *op)
- {
- struct orangefs_dir_part *part, *new;
- size_t count;
- count = 1;
- part = od->part;
- while (part) {
- count++;
- if (part->next)
- part = part->next;
- else
- break;
- }
- new = (void *)op->downcall.trailer_buf;
- new->next = NULL;
- new->len = op->downcall.trailer_size -
- sizeof(struct orangefs_readdir_response_s);
- if (!od->part)
- od->part = new;
- else
- part->next = new;
- count++;
- od->end = count << PART_SHIFT;
- return 0;
- }
- static int orangefs_dir_more(struct orangefs_inode_s *oi,
- struct orangefs_dir *od, struct dentry *dentry)
- {
- struct orangefs_kernel_op_s *op;
- int r;
- op = op_alloc(ORANGEFS_VFS_OP_READDIR);
- if (!op) {
- od->error = -ENOMEM;
- return -ENOMEM;
- }
- r = do_readdir(oi, od, dentry, op);
- if (r) {
- od->error = r;
- goto out;
- }
- r = parse_readdir(od, op);
- if (r) {
- od->error = r;
- goto out;
- }
- od->error = 0;
- out:
- op_release(op);
- return od->error;
- }
- static int fill_from_part(struct orangefs_dir_part *part,
- struct dir_context *ctx)
- {
- const int offset = sizeof(struct orangefs_readdir_response_s);
- struct orangefs_khandle *khandle;
- __u32 *len, padlen;
- loff_t i;
- char *s;
- i = ctx->pos & ~PART_MASK;
- /* The file offset from userspace is too large. */
- if (i > part->len)
- return 1;
- /*
- * If the seek pointer is positioned just before an entry it
- * should find the next entry.
- */
- if (i % 8)
- i = i + (8 - i%8)%8;
- while (i < part->len) {
- if (part->len < i + sizeof *len)
- break;
- len = (void *)part + offset + i;
- /*
- * len is the size of the string itself. padlen is the
- * total size of the encoded string.
- */
- padlen = (sizeof *len + *len + 1) +
- (8 - (sizeof *len + *len + 1)%8)%8;
- if (part->len < i + padlen + sizeof *khandle)
- goto next;
- s = (void *)part + offset + i + sizeof *len;
- if (s[*len] != 0)
- goto next;
- khandle = (void *)part + offset + i + padlen;
- if (!dir_emit(ctx, s, *len,
- orangefs_khandle_to_ino(khandle),
- DT_UNKNOWN))
- return 0;
- i += padlen + sizeof *khandle;
- i = i + (8 - i%8)%8;
- BUG_ON(i > part->len);
- ctx->pos = (ctx->pos & PART_MASK) | i;
- continue;
- next:
- i += 8;
- }
- return 1;
- }
- static int orangefs_dir_fill(struct orangefs_inode_s *oi,
- struct orangefs_dir *od, struct dentry *dentry,
- struct dir_context *ctx)
- {
- struct orangefs_dir_part *part;
- size_t count;
- count = ((ctx->pos & PART_MASK) >> PART_SHIFT) - 1;
- part = od->part;
- while (part->next && count) {
- count--;
- part = part->next;
- }
- /* This means the userspace file offset is invalid. */
- if (count) {
- od->error = -EIO;
- return -EIO;
- }
- while (part && part->len) {
- int r;
- r = fill_from_part(part, ctx);
- if (r < 0) {
- od->error = r;
- return r;
- } else if (r == 0) {
- /* Userspace buffer is full. */
- break;
- } else {
- /*
- * The part ran out of data. Move to the next
- * part. */
- ctx->pos = (ctx->pos & PART_MASK) +
- (1 << PART_SHIFT);
- part = part->next;
- }
- }
- return 0;
- }
- static loff_t orangefs_dir_llseek(struct file *file, loff_t offset,
- int whence)
- {
- struct orangefs_dir *od = file->private_data;
- /*
- * Delete the stored data so userspace sees new directory
- * entries.
- */
- if (!whence && offset < od->end) {
- struct orangefs_dir_part *part = od->part;
- while (part) {
- struct orangefs_dir_part *next = part->next;
- vfree(part);
- part = next;
- }
- od->token = ORANGEFS_ITERATE_START;
- od->part = NULL;
- od->end = 1 << PART_SHIFT;
- }
- return default_llseek(file, offset, whence);
- }
- static int orangefs_dir_iterate(struct file *file,
- struct dir_context *ctx)
- {
- struct orangefs_inode_s *oi;
- struct orangefs_dir *od;
- struct dentry *dentry;
- int r;
- dentry = file->f_path.dentry;
- oi = ORANGEFS_I(dentry->d_inode);
- od = file->private_data;
- if (od->error)
- return od->error;
- if (ctx->pos == 0) {
- if (!dir_emit_dot(file, ctx))
- return 0;
- ctx->pos++;
- }
- if (ctx->pos == 1) {
- if (!dir_emit_dotdot(file, ctx))
- return 0;
- ctx->pos = 1 << PART_SHIFT;
- }
- /*
- * The seek position is in the first synthesized part but is not
- * valid.
- */
- if ((ctx->pos & PART_MASK) == 0)
- return -EIO;
- r = 0;
- /*
- * Must read more if the user has sought past what has been read
- * so far. Stop a user who has sought past the end.
- */
- while (od->token != ORANGEFS_ITERATE_END &&
- ctx->pos > od->end) {
- r = orangefs_dir_more(oi, od, dentry);
- if (r)
- return r;
- }
- if (od->token == ORANGEFS_ITERATE_END && ctx->pos > od->end)
- return -EIO;
- /* Then try to fill if there's any left in the buffer. */
- if (ctx->pos < od->end) {
- r = orangefs_dir_fill(oi, od, dentry, ctx);
- if (r)
- return r;
- }
- /* Finally get some more and try to fill. */
- if (od->token != ORANGEFS_ITERATE_END) {
- r = orangefs_dir_more(oi, od, dentry);
- if (r)
- return r;
- r = orangefs_dir_fill(oi, od, dentry, ctx);
- }
- return r;
- }
- static int orangefs_dir_open(struct inode *inode, struct file *file)
- {
- struct orangefs_dir *od;
- file->private_data = kmalloc(sizeof(struct orangefs_dir),
- GFP_KERNEL);
- if (!file->private_data)
- return -ENOMEM;
- od = file->private_data;
- od->token = ORANGEFS_ITERATE_START;
- od->part = NULL;
- od->end = 1 << PART_SHIFT;
- od->error = 0;
- return 0;
- }
- static int orangefs_dir_release(struct inode *inode, struct file *file)
- {
- struct orangefs_dir *od = file->private_data;
- struct orangefs_dir_part *part = od->part;
- while (part) {
- struct orangefs_dir_part *next = part->next;
- vfree(part);
- part = next;
- }
- kfree(od);
- return 0;
- }
- const struct file_operations orangefs_dir_operations = {
- .llseek = orangefs_dir_llseek,
- .read = generic_read_dir,
- .iterate = orangefs_dir_iterate,
- .open = orangefs_dir_open,
- .release = orangefs_dir_release
- };
|