diff options
author | Jeremie Koenig <jk@jk.fr.eu.org> | 2010-08-17 09:43:29 +0000 |
---|---|---|
committer | Jeremie Koenig <jk@jk.fr.eu.org> | 2010-08-30 14:14:42 +0200 |
commit | d938e96e59a41d5eaa11040513815b757e58eb0c (patch) | |
tree | 38d1eb581cfeace88ebcd475b44f1a1459654392 |
Basic infrastructure
* procfs.h: New file; basic interfaces for procfs nodes.
* procfs.c: New file; implement the basic infrastructure.
* netfs.c: New file; bridge libnetfs and the procfs interfaces.
* main.c: New file; mostly a "Hello, World!" for now.
* Makefile: New file; standalone for now.
-rw-r--r-- | Makefile | 17 | ||||
-rw-r--r-- | main.c | 58 | ||||
-rw-r--r-- | netfs.c | 391 | ||||
-rw-r--r-- | procfs.c | 90 | ||||
-rw-r--r-- | procfs.h | 40 |
5 files changed, 596 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..4ae336a3 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +TARGET = procfs +OBJS = procfs.o netfs.o main.o +LIBS = -lnetfs + +CC = gcc +CFLAGS = -Wall -g +CPPFLAGS = + +CPPFLAGS += -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) + +clean: + $(RM) $(TARGET) $(OBJS) @@ -0,0 +1,58 @@ +#include <mach.h> +#include <error.h> +#include <argp.h> +#include <hurd/netfs.h> +#include "procfs.h" + +static error_t get_contents (void *hook, void **contents, size_t *contents_len) +{ + static const char hello[] = "Hello, World!\n"; + *contents = (void *) hello; + *contents_len = sizeof hello - 1; + return 0; +} + +static error_t get_entries (void *hook, void **contents, size_t *contents_len) +{ + static const char entries[] = "hello"; + *contents = (void *) entries; + *contents_len = sizeof entries; + return 0; +} + +static error_t lookup (void *hook, const char *name, struct node **np) +{ + static const struct procfs_node_ops ops = { .get_contents = get_contents }; + + if (strcmp (name, "hello")) + return ENOENT; + + *np = procfs_make_node (&ops, NULL); + if (! *np) + return ENOMEM; + + return 0; +} + +int main (int argc, char **argv) +{ + static const struct procfs_node_ops ops = { + .get_contents = get_entries, + .lookup = lookup, + }; + mach_port_t bootstrap; + + argp_parse (&netfs_std_startup_argp, argc, argv, 0, 0, 0); + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + if (bootstrap == MACH_PORT_NULL) + error (1, 0, "Must be started as a translator"); + + netfs_init (); + netfs_root_node = procfs_make_node (&ops, NULL); + + netfs_startup (bootstrap, 0); + for (;;) + netfs_server_loop (); +} + diff --git a/netfs.c b/netfs.c new file mode 100644 index 00000000..58665085 --- /dev/null +++ b/netfs.c @@ -0,0 +1,391 @@ +#include <hurd/netfs.h> +#include <hurd/fshelp.h> +#include <sys/mman.h> +#include <mach/vm_param.h> +#include <dirent.h> +#include <fcntl.h> +#include "procfs.h" + +#define PROCFS_SERVER_NAME "procfs" +#define PROCFS_SERVER_VERSION "0.1.0" +#define PROCFS_MAXSYMLINKS 16 + + +/* Interesting libnetfs callback functions. */ + +/* The user must define this variable. Set this to the name of the + filesystem server. */ +char *netfs_server_name = PROCFS_SERVER_NAME; + +/* The user must define this variables. Set this to be the server + version number. */ +char *netfs_server_version = PROCFS_SERVER_VERSION; + +/* Maximum number of symlinks to follow before returning ELOOP. */ +int netfs_maxsymlinks = PROCFS_MAXSYMLINKS; + +/* The user must define this function. Read from the locked file NP + for user CRED starting at OFFSET and continuing for up to *LEN + bytes. Put the data at DATA. Set *LEN to the amount successfully + read upon return. */ +error_t netfs_attempt_read (struct iouser *cred, struct node *np, + loff_t offset, size_t *len, void *data) +{ + char *contents; + size_t contents_len; + error_t err; + + err = procfs_get_contents (np, (void **) &contents, &contents_len); + if (err) + return err; + + contents += offset; + contents_len -= offset; + + if (*len > contents_len) + *len = contents_len; + if (*len < 0) + *len = 0; + + memcpy (data, contents, *len); + return 0; +} + +/* The user must define this function. Read the contents of locked + node NP (a symlink), for USER, into BUF. */ +error_t netfs_attempt_readlink (struct iouser *user, struct node *np, + char *buf) +{ + return EIO; +} + +/* Helper function for netfs_get_dirents() below. CONTENTS is an argz + vector of directory entry names, as returned by procfs_get_contents(). + Convert at most NENTRIES of them to dirent structures, put them in + DATA (if not NULL), write the number of entries processed in *AMT and + return the required/used space in DATACNT. */ +static int putentries (char *contents, size_t contents_len, int nentries, + char *data, mach_msg_type_number_t *datacnt) +{ + int i; + + *datacnt = 0; + for (i = 0; contents_len && (nentries < 0 || i < nentries); i++) + { + int namlen = strlen (contents); + int reclen = sizeof (struct dirent) + namlen; + + if (data) + { + struct dirent *d = (struct dirent *) (data + *datacnt); + d->d_fileno = 42; /* XXX */ + d->d_namlen = namlen; + d->d_reclen = reclen; + d->d_type = DT_UNKNOWN; + strcpy (d->d_name, contents); + } + + *datacnt += reclen; + contents += namlen + 1; + contents_len -= namlen + 1; + } + + return i; +} + +/* The user must define this function. Fill the array *DATA of size + BUFSIZE with up to NENTRIES dirents from DIR (which is locked) + starting with entry ENTRY for user CRED. The number of entries in + the array is stored in *AMT and the number of bytes in *DATACNT. + If the supplied buffer is not large enough to hold the data, it + should be grown. */ +error_t netfs_get_dirents (struct iouser *cred, struct node *dir, + int entry, int nentries, char **data, + mach_msg_type_number_t *datacnt, + vm_size_t bufsize, int *amt) +{ + char *contents; + size_t contents_len; + error_t err; + + err = procfs_get_contents (dir, (void **) &contents, &contents_len); + if (err) + return err; + + /* We depend on the fact that CONTENTS is terminated. */ + assert (contents_len == 0 || contents[contents_len - 1] == '\0'); + + /* Skip to the first requested entry. */ + while (contents_len && entry--) + { + int ofs = strlen (contents) + 1; + contents += ofs; + contents_len -= ofs; + } + + /* Allocate a buffer if necessary. */ + putentries (contents, contents_len, nentries, NULL, datacnt); + if (bufsize < *datacnt) + { + void *n = mmap (0, *datacnt, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, 0, 0); + if (n == MAP_FAILED) + return ENOMEM; + + *data = n; + } + + /* Do the actual conversion. */ + *amt = putentries (contents, contents_len, nentries, *data, datacnt); + + return 0; +} + +/* The user must define this function. Lookup NAME in DIR (which is + locked) for USER; set *NP to the found name upon return. If the + name was not found, then return ENOENT. On any error, clear *NP. + (*NP, if found, should be locked and a reference to it generated. + This call should unlock DIR no matter what.) */ +error_t netfs_attempt_lookup (struct iouser *user, struct node *dir, + char *name, struct node **np) +{ + error_t err; + + err = procfs_lookup (dir, name, np); + if (! err) + mutex_lock (&(*np)->lock); + + mutex_unlock (&dir->lock); + return err; +} + +/* The user must define this function. Node NP has no more references; + free all its associated storage. */ +void netfs_node_norefs (struct node *np) +{ + procfs_cleanup (np); + free (np); +} + + +/* Libnetfs callbacks managed with libfshelp. */ + +/* The user must define this function. Locked node NP is being opened + by USER, with FLAGS. NEWNODE is nonzero if we just created this + node. Return an error if we should not permit the open to complete + because of a permission restriction. */ +error_t netfs_check_open_permissions (struct iouser *user, struct node *np, + int flags, int newnode) +{ + error_t err = 0; + if (!err && (flags & O_READ)) + err = fshelp_access (&np->nn_stat, S_IREAD, user); + if (!err && (flags & O_WRITE)) + err = fshelp_access (&np->nn_stat, S_IWRITE, user); + if (!err && (flags & O_EXEC)) + err = fshelp_access (&np->nn_stat, S_IEXEC, user); + return err; +} + +/* The user must define this function. Return the valid access + types (bitwise OR of O_READ, O_WRITE, and O_EXEC) in *TYPES for + locked file NP and user CRED. */ +error_t netfs_report_access (struct iouser *cred, struct node *np, + int *types) +{ + *types = 0; + if (fshelp_access (&np->nn_stat, S_IREAD, cred) == 0) + *types |= O_READ; + if (fshelp_access (&np->nn_stat, S_IWRITE, cred) == 0) + *types |= O_WRITE; + if (fshelp_access (&np->nn_stat, S_IEXEC, cred) == 0) + *types |= O_EXEC; + return 0; +} + + +/* Trivial or unsupported libnetfs callbacks. */ + +/* The user must define this function. Make sure that NP->nn_stat is + filled with the most current information. CRED identifies the user + responsible for the operation. NP is locked. */ +error_t netfs_validate_stat (struct node *np, struct iouser *cred) +{ + return 0; +} + +/* The user must define this function. This should attempt a chmod + call for the user specified by CRED on locked node NP, to change + the owner to UID and the group to GID. */ +error_t netfs_attempt_chown (struct iouser *cred, struct node *np, + uid_t uid, uid_t gid) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a chauthor + call for the user specified by CRED on locked node NP, thereby + changing the author to AUTHOR. */ +error_t netfs_attempt_chauthor (struct iouser *cred, struct node *np, + uid_t author) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a chmod + call for the user specified by CRED on locked node NODE, to change + the mode to MODE. Unlike the normal Unix and Hurd meaning of + chmod, this function is also used to attempt to change files into + other types. If such a transition is attempted which is + impossible, then return EOPNOTSUPP. */ +error_t netfs_attempt_chmod (struct iouser *cred, struct node *np, + mode_t mode) +{ + return EROFS; +} + +/* The user must define this function. Attempt to turn locked node NP + (user CRED) into a symlink with target NAME. */ +error_t netfs_attempt_mksymlink (struct iouser *cred, struct node *np, + char *name) +{ + return EROFS; +} + +/* The user must define this function. Attempt to turn NODE (user + CRED) into a device. TYPE is either S_IFBLK or S_IFCHR. NP is + locked. */ +error_t netfs_attempt_mkdev (struct iouser *cred, struct node *np, + mode_t type, dev_t indexes) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a chflags + call for the user specified by CRED on locked node NP, to change + the flags to FLAGS. */ +error_t netfs_attempt_chflags (struct iouser *cred, struct node *np, + int flags) +{ + return EROFS; +} + +/* The user must define this function. This should attempt a utimes + call for the user specified by CRED on locked node NP, to change + the atime to ATIME and the mtime to MTIME. If ATIME or MTIME is + null, then set to the current time. */ +error_t netfs_attempt_utimes (struct iouser *cred, struct node *np, + struct timespec *atime, struct timespec *mtime) +{ + return EROFS; +} + +/* The user must define this function. This should attempt to set the + size of the locked file NP (for user CRED) to SIZE bytes long. */ +error_t netfs_attempt_set_size (struct iouser *cred, struct node *np, + loff_t size) +{ + return EROFS; +} + +/* The user must define this function. This should attempt to fetch + filesystem status information for the remote filesystem, for the + user CRED. NP is locked. */ +error_t netfs_attempt_statfs (struct iouser *cred, struct node *np, + fsys_statfsbuf_t *st) +{ + return ENOSYS; +} + +/* The user must define this function. This should sync the locked + file NP completely to disk, for the user CRED. If WAIT is set, + return only after the sync is completely finished. */ +error_t netfs_attempt_sync (struct iouser *cred, struct node *np, + int wait) +{ + return 0; +} + +/* The user must define this function. This should sync the entire + remote filesystem. If WAIT is set, return only after the sync is + completely finished. */ +error_t netfs_attempt_syncfs (struct iouser *cred, int wait) +{ + return 0; +} + +/* The user must define this function. Delete NAME in DIR (which is + locked) for USER. */ +error_t netfs_attempt_unlink (struct iouser *user, struct node *dir, + char *name) +{ + return EROFS; +} + +/* The user must define this function. Attempt to rename the + directory FROMDIR to TODIR. Note that neither of the specific nodes + are locked. */ +error_t netfs_attempt_rename (struct iouser *user, struct node *fromdir, + char *fromname, struct node *todir, + char *toname, int excl) +{ + return EROFS; +} + +/* The user must define this function. Attempt to create a new + directory named NAME in DIR (which is locked) for USER with mode + MODE. */ +error_t netfs_attempt_mkdir (struct iouser *user, struct node *dir, + char *name, mode_t mode) +{ + return EROFS; +} + +/* The user must define this function. Attempt to remove directory + named NAME in DIR (which is locked) for USER. */ +error_t netfs_attempt_rmdir (struct iouser *user, + struct node *dir, char *name) +{ + return EROFS; +} + + +/* The user must define this function. Create a link in DIR with name + NAME to FILE for USER. Note that neither DIR nor FILE are + locked. If EXCL is set, do not delete the target. Return EEXIST if + NAME is already found in DIR. */ +error_t netfs_attempt_link (struct iouser *user, struct node *dir, + struct node *file, char *name, int excl) +{ + return EROFS; +} + +/* The user must define this function. Attempt to create an anonymous + file related to DIR (which is locked) for USER with MODE. Set *NP + to the returned file upon success. No matter what, unlock DIR. */ +error_t netfs_attempt_mkfile (struct iouser *user, struct node *dir, + mode_t mode, struct node **np) +{ + return EROFS; +} + +/* The user must define this function. Attempt to create a file named + NAME in DIR (which is locked) for USER with MODE. Set *NP to the + new node upon return. On any error, clear *NP. *NP should be + locked on success; no matter what, unlock DIR before returning. */ +error_t netfs_attempt_create_file (struct iouser *user, struct node *dir, + char *name, mode_t mode, struct node **np) +{ + return EROFS; +} + +/* The user must define this function. Write to the locked file NP + for user CRED starting at OFSET and continuing for up to *LEN bytes + from DATA. Set *LEN to the amount successfully written upon + return. */ +error_t netfs_attempt_write (struct iouser *cred, struct node *np, + loff_t offset, size_t *len, void *data) +{ + return EROFS; +} + + diff --git a/procfs.c b/procfs.c new file mode 100644 index 00000000..304befba --- /dev/null +++ b/procfs.c @@ -0,0 +1,90 @@ +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <hurd/netfs.h> +#include <hurd/fshelp.h> +#include "procfs.h" + +struct netnode +{ + const struct procfs_node_ops *ops; + void *hook; + + /* (cached) contents of the node */ + void *contents; + size_t contents_len; +}; + +struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook) +{ + struct netnode *nn; + struct node *np; + + nn = malloc (sizeof *nn); + if (! nn) + return NULL; + + memset (nn, 0, sizeof *nn); + nn->ops = ops; + nn->hook = hook; + + np = netfs_make_node (nn); + if (! np) + { + free (nn); + return NULL; + } + + np->nn = nn; + memset (&np->nn_stat, 0, sizeof np->nn_stat); + np->nn_translated = 0; + + if (np->nn->ops->lookup) + np->nn_stat.st_mode = S_IFDIR | 0555; + else + np->nn_stat.st_mode = S_IFREG | 0444; + + return np; +} + +error_t procfs_get_contents (struct node *np, void **data, size_t *data_len) +{ + if (! np->nn->contents && np->nn->ops->get_contents) + { + void *contents; + size_t contents_len; + error_t err; + + err = np->nn->ops->get_contents (np->nn->hook, &contents, &contents_len); + if (err) + return err; + + np->nn->contents = contents; + np->nn->contents_len = contents_len; + } + + *data = np->nn->contents; + *data_len = np->nn->contents_len; + return 0; +} + +error_t procfs_lookup (struct node *np, const char *name, struct node **npp) +{ + error_t err = ENOENT; + + if (np->nn->ops->lookup) + err = np->nn->ops->lookup (np->nn->hook, name, npp); + + return err; +} + +void procfs_cleanup (struct node *np) +{ + if (np->nn->contents && np->nn->ops->cleanup_contents) + np->nn->ops->cleanup_contents (np->nn->contents); + + if (np->nn->ops->cleanup) + np->nn->ops->cleanup (np->nn->hook); + + free (np->nn); +} diff --git a/procfs.h b/procfs.h new file mode 100644 index 00000000..0557b6d1 --- /dev/null +++ b/procfs.h @@ -0,0 +1,40 @@ +#include <hurd/hurd_types.h> +#include <hurd/netfs.h> + + +/* Interface for the procfs side. */ + +/* Any of these callback functions can be omitted, in which case + reasonable defaults will be used. The initial file mode and type + depend on whether a lookup function is provided, but can be + overridden in update_stat(). */ +struct procfs_node_ops +{ + /* Fetch the contents of a node. A pointer to the contents should be + returned in *CONTENTS and their length in *CONTENTS_LEN. The exact + nature of these data depends on whether the node is a regular file, + symlink or directory, as determined by the file mode in + netnode->nn_stat. For regular files and symlinks, they are what + you would expect; for directories, they are an argz vector of the + names of the entries. */ + error_t (*get_contents) (void *hook, void **contents, size_t *contents_len); + void (*cleanup_contents) (void *contents); + + /* Lookup NAME in this directory, and store the result in *np. The + returned node should be created by lookup() using procfs_make_node() + or a derived function. */ + error_t (*lookup) (void *hook, const char *name, struct node **np); + + /* Destroy this node. */ + void (*cleanup) (void *hook); +}; + +struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook); + + +/* Interface for the libnetfs side. */ + +error_t procfs_get_contents (struct node *np, void **data, size_t *data_len); +error_t procfs_lookup (struct node *np, const char *name, struct node **npp); +void procfs_cleanup (struct node *np); + |