mirror of
https://github.com/libfuse/libfuse.git
synced 2024-11-23 12:14:15 +08:00
f448ac69d1
This patch implements readdirplus support in FUSE usersapce. It adds a new fuse lowlevel operations fuse_lowleve_ops::readdir_plus, corespoding mount options and helper functions to maintain buffer. [From: Eric Wong <normalperson@yhbt.net>] This makes our terminology consistent with NFS and our kernel module, as well as reducing user/developer confusion in the command-line. Note: I'm keeping "fuse_add_direntry_plus" since that is less standardized in its use than "readdirplus" for now. Signed-off-by: Feng Shuo <steve.shuo.feng@gmail.com>
199 lines
5.1 KiB
C
199 lines
5.1 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "fuse_kernel.h"
|
|
|
|
static struct {
|
|
const char *name;
|
|
} fuse_ll_ops[] = {
|
|
[FUSE_LOOKUP] = { "LOOKUP" },
|
|
[FUSE_FORGET] = { "FORGET" },
|
|
[FUSE_GETATTR] = { "GETATTR" },
|
|
[FUSE_SETATTR] = { "SETATTR" },
|
|
[FUSE_READLINK] = { "READLINK" },
|
|
[FUSE_SYMLINK] = { "SYMLINK" },
|
|
[FUSE_MKNOD] = { "MKNOD" },
|
|
[FUSE_MKDIR] = { "MKDIR" },
|
|
[FUSE_UNLINK] = { "UNLINK" },
|
|
[FUSE_RMDIR] = { "RMDIR" },
|
|
[FUSE_RENAME] = { "RENAME" },
|
|
[FUSE_LINK] = { "LINK" },
|
|
[FUSE_OPEN] = { "OPEN" },
|
|
[FUSE_READ] = { "READ" },
|
|
[FUSE_WRITE] = { "WRITE" },
|
|
[FUSE_STATFS] = { "STATFS" },
|
|
[FUSE_RELEASE] = { "RELEASE" },
|
|
[FUSE_FSYNC] = { "FSYNC" },
|
|
[FUSE_SETXATTR] = { "SETXATTR" },
|
|
[FUSE_GETXATTR] = { "GETXATTR" },
|
|
[FUSE_LISTXATTR] = { "LISTXATTR" },
|
|
[FUSE_REMOVEXATTR] = { "REMOVEXATTR" },
|
|
[FUSE_FLUSH] = { "FLUSH" },
|
|
[FUSE_INIT] = { "INIT" },
|
|
[FUSE_OPENDIR] = { "OPENDIR" },
|
|
[FUSE_READDIR] = { "READDIR" },
|
|
[FUSE_RELEASEDIR] = { "RELEASEDIR" },
|
|
[FUSE_FSYNCDIR] = { "FSYNCDIR" },
|
|
[FUSE_GETLK] = { "GETLK" },
|
|
[FUSE_SETLK] = { "SETLK" },
|
|
[FUSE_SETLKW] = { "SETLKW" },
|
|
[FUSE_ACCESS] = { "ACCESS" },
|
|
[FUSE_CREATE] = { "CREATE" },
|
|
[FUSE_INTERRUPT] = { "INTERRUPT" },
|
|
[FUSE_BMAP] = { "BMAP" },
|
|
[FUSE_DESTROY] = { "DESTROY" },
|
|
[FUSE_READDIRPLUS] = { "READDIRPLUS" },
|
|
};
|
|
|
|
#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
|
|
|
|
static const char *opname(enum fuse_opcode opcode)
|
|
{
|
|
if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
|
|
return "???";
|
|
else
|
|
return fuse_ll_ops[opcode].name;
|
|
}
|
|
|
|
|
|
static void process_buf(int dir, char *buf, int len)
|
|
{
|
|
static unsigned long long prevuniq = -1;
|
|
static int prevopcode;
|
|
|
|
if (!dir) {
|
|
struct fuse_in_header *in = (struct fuse_in_header *) buf;
|
|
buf += sizeof(struct fuse_in_header);
|
|
|
|
printf("unique: %llu, opcode: %s (%i), nodeid: %lu, len: %i, insize: %i\n",
|
|
(unsigned long long) in->unique,
|
|
opname((enum fuse_opcode) in->opcode), in->opcode,
|
|
(unsigned long) in->nodeid, in->len, len);
|
|
|
|
switch (in->opcode) {
|
|
case FUSE_READ: {
|
|
struct fuse_read_in *arg = (struct fuse_read_in *) buf;
|
|
printf("-READ fh:%llu off:%llu siz:%u rfl:%u own:%llu fl:%u\n",
|
|
arg->fh, arg->offset, arg->size, arg->read_flags,
|
|
arg->lock_owner, arg->flags);
|
|
break;
|
|
}
|
|
case FUSE_WRITE: {
|
|
struct fuse_write_in *arg = (struct fuse_write_in *) buf;
|
|
printf("-WRITE fh:%llu off:%llu siz:%u wfl:%u own:%llu fl:%u\n",
|
|
arg->fh, arg->offset, arg->size, arg->write_flags,
|
|
arg->lock_owner, arg->flags);
|
|
break;
|
|
}
|
|
}
|
|
prevuniq = in->unique;
|
|
prevopcode = in->opcode;
|
|
} else {
|
|
struct fuse_out_header *out = (struct fuse_out_header *) buf;
|
|
buf += sizeof(struct fuse_out_header);
|
|
|
|
printf(" unique: %llu, error: %i (%s), len: %i, outsize: %i\n",
|
|
(unsigned long long) out->unique, out->error,
|
|
strerror(-out->error), out->len, len);
|
|
|
|
if (out->unique == prevuniq) {
|
|
switch (prevopcode) {
|
|
case FUSE_GETATTR: {
|
|
struct fuse_attr_out *arg = (struct fuse_attr_out *) buf;
|
|
printf("+ATTR v:%llu.%09u i:%llu s:%llu b:%llu\n",
|
|
arg->attr_valid, arg->attr_valid_nsec,
|
|
arg->attr.ino, arg->attr.size, arg->attr.blocks);
|
|
break;
|
|
}
|
|
case FUSE_LOOKUP: {
|
|
struct fuse_entry_out *arg = (struct fuse_entry_out *) buf;
|
|
printf("+ENTRY nodeid:%llu v:%llu.%09u i:%llu s:%llu b:%llu\n",
|
|
arg->nodeid, arg->attr_valid, arg->attr_valid_nsec,
|
|
arg->attr.ino, arg->attr.size, arg->attr.blocks);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
FILE *in = stdin;
|
|
while (1) {
|
|
int dir;
|
|
int res;
|
|
char buf[1048576];
|
|
unsigned len = 0;
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
while (1) {
|
|
char str[32];
|
|
|
|
res = fscanf(in, "%30s", str);
|
|
if (res != 1 && feof(in))
|
|
return 0;
|
|
|
|
if (res == 0)
|
|
continue;
|
|
|
|
if (strncmp(str, "read(", 5) == 0) {
|
|
dir = 0;
|
|
break;
|
|
} else if (strncmp(str, "writev(", 7) == 0) {
|
|
dir = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (1) {
|
|
int c = getc(in);
|
|
if (c == '"') {
|
|
while (1) {
|
|
int val;
|
|
|
|
c = getc(in);
|
|
if (c == EOF) {
|
|
fprintf(stderr, "eof in string\n");
|
|
break;
|
|
}
|
|
if (c == '\n') {
|
|
fprintf(stderr, "eol in string\n");
|
|
break;
|
|
}
|
|
if (c == '"')
|
|
break;
|
|
if (c != '\\') {
|
|
val = c;
|
|
} else {
|
|
c = getc(in);
|
|
switch (c) {
|
|
case 'n': val = '\n'; break;
|
|
case 'r': val = '\r'; break;
|
|
case 't': val = '\t'; break;
|
|
case '"': val = '"'; break;
|
|
case '\\': val = '\\'; break;
|
|
case 'x':
|
|
res = scanf("%x", &val);
|
|
if (res != 1) {
|
|
fprintf(stderr, "parse error\n");
|
|
continue;
|
|
}
|
|
break;
|
|
default:
|
|
fprintf(stderr, "unknown sequence: '\\%c'\n", c);
|
|
continue;
|
|
}
|
|
}
|
|
buf[len++] = val;
|
|
}
|
|
}
|
|
if (c == '\n')
|
|
break;
|
|
}
|
|
process_buf(dir, buf, len);
|
|
memset(buf, 0, len);
|
|
len = 0;
|
|
}
|
|
}
|