From bb4d055fa6d95d5baaa62fcc63bba7eba03f7f73 Mon Sep 17 00:00:00 2001 From: David van Moolenbroek Date: Mon, 9 Apr 2012 19:20:37 +0200 Subject: [PATCH] Add libvboxfs: VirtualBox shared folders library --- include/Makefile.minix.inc | 2 +- include/minix/vboxfs.h | 12 ++ lib/Makefile | 2 +- lib/libvboxfs/Makefile | 8 ++ lib/libvboxfs/attr.c | 169 +++++++++++++++++++++++++ lib/libvboxfs/dir.c | 245 +++++++++++++++++++++++++++++++++++++ lib/libvboxfs/file.c | 119 ++++++++++++++++++ lib/libvboxfs/glo.h | 9 ++ lib/libvboxfs/handle.c | 121 ++++++++++++++++++ lib/libvboxfs/inc.h | 18 +++ lib/libvboxfs/info.c | 58 +++++++++ lib/libvboxfs/link.c | 109 +++++++++++++++++ lib/libvboxfs/path.c | 60 +++++++++ lib/libvboxfs/proto.h | 46 +++++++ lib/libvboxfs/vboxfs.c | 98 +++++++++++++++ lib/libvboxfs/vboxfs.h | 137 +++++++++++++++++++++ share/mk/bsd.prog.mk | 2 +- 17 files changed, 1212 insertions(+), 3 deletions(-) create mode 100644 include/minix/vboxfs.h create mode 100644 lib/libvboxfs/Makefile create mode 100644 lib/libvboxfs/attr.c create mode 100644 lib/libvboxfs/dir.c create mode 100644 lib/libvboxfs/file.c create mode 100644 lib/libvboxfs/glo.h create mode 100644 lib/libvboxfs/handle.c create mode 100644 lib/libvboxfs/inc.h create mode 100644 lib/libvboxfs/info.c create mode 100644 lib/libvboxfs/link.c create mode 100644 lib/libvboxfs/path.c create mode 100644 lib/libvboxfs/proto.h create mode 100644 lib/libvboxfs/vboxfs.c create mode 100644 lib/libvboxfs/vboxfs.h diff --git a/include/Makefile.minix.inc b/include/Makefile.minix.inc index d095e992d..5bf445f63 100644 --- a/include/Makefile.minix.inc +++ b/include/Makefile.minix.inc @@ -18,7 +18,7 @@ INCS+= minix/acpi.h minix/audio_fw.h minix/bitmap.h \ minix/sound.h minix/spin.h minix/sys_config.h minix/sysinfo.h \ minix/syslib.h minix/sysutil.h minix/timers.h minix/type.h \ minix/tty.h minix/u64.h minix/usb.h minix/usb_ch9.h minix/vbox.h \ - minix/vboxif.h minix/vboxtype.h minix/vm.h \ + minix/vboxfs.h minix/vboxif.h minix/vboxtype.h minix/vm.h \ minix/vfsif.h minix/vtreefs.h minix/libminixfs.h minix/netsock.h INCS+= net/gen/arp_io.h net/gen/dhcp.h net/gen/ether.h \ diff --git a/include/minix/vboxfs.h b/include/minix/vboxfs.h new file mode 100644 index 000000000..20a889c36 --- /dev/null +++ b/include/minix/vboxfs.h @@ -0,0 +1,12 @@ +/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */ + +#ifndef _MINIX_VBOXFS_H +#define _MINIX_VBOXFS_H + +#include + +int vboxfs_init(char *share, const struct sffs_table **tablep, + int *case_insens, int *read_only); +void vboxfs_cleanup(void); + +#endif /* _MINIX_VBOXFS_H */ diff --git a/lib/Makefile b/lib/Makefile index 86c4c9f84..30a7b869f 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -6,7 +6,7 @@ SUBDIR= csu libcompat_minix libc libblockdriver libchardriver \ libexec libdevman libusb libminlib libasyn \ libddekit libminixfs libbdev libelf libminc libcrypt libterminfo \ libcurses libvassert libutil libpuffs librefuse libbz2 libarchive \ - libprop libnetsock libsffs libhgfs + libprop libnetsock libsffs libhgfs libvboxfs SUBDIR+= ../external/public-domain/xz/lib diff --git a/lib/libvboxfs/Makefile b/lib/libvboxfs/Makefile new file mode 100644 index 000000000..908c336a7 --- /dev/null +++ b/lib/libvboxfs/Makefile @@ -0,0 +1,8 @@ +# Makefile for libvboxfs +.include + +LIB= vboxfs + +SRCS= attr.c dir.c file.c handle.c info.c link.c path.c vboxfs.c + +.include diff --git a/lib/libvboxfs/attr.c b/lib/libvboxfs/attr.c new file mode 100644 index 000000000..ba3662cc4 --- /dev/null +++ b/lib/libvboxfs/attr.c @@ -0,0 +1,169 @@ +/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */ + +#include "inc.h" + +/* + * Convert a VirtualBox timestamp to a POSIX timespec structure. + * VirtualBox' timestamps are in nanoseconds since the UNIX epoch. + */ +static void +get_time(struct timespec *tsp, u64_t nsecs) +{ + + tsp->tv_sec = div64u(nsecs, 1000000000); + tsp->tv_nsec = rem64u(nsecs, 1000000000); +} + +/* + * Convert a POSIX timespec structure to a VirtualBox timestamp. + */ +static u64_t +set_time(struct timespec *tsp) +{ + + return add64u(mul64u(tsp->tv_sec, 1000000000), tsp->tv_nsec); +} + +/* + * Fill the given attribute structure with VirtualBox object information. + */ +void +vboxfs_get_attr(struct sffs_attr *attr, vboxfs_objinfo_t *info) +{ + + if (attr->a_mask & SFFS_ATTR_SIZE) + attr->a_size = info->size; + if (attr->a_mask & SFFS_ATTR_MODE) + attr->a_mode = VBOXFS_GET_MODE(info->attr.mode); + if (attr->a_mask & SFFS_ATTR_ATIME) + get_time(&attr->a_atime, info->atime); + if (attr->a_mask & SFFS_ATTR_MTIME) + get_time(&attr->a_mtime, info->mtime); + if (attr->a_mask & SFFS_ATTR_CTIME) + get_time(&attr->a_ctime, info->ctime); + if (attr->a_mask & SFFS_ATTR_CRTIME) + get_time(&attr->a_crtime, info->crtime); +} + +/* + * Get file attributes. + */ +int +vboxfs_getattr(char *path, struct sffs_attr *attr) +{ + vbox_param_t param[3]; + vboxfs_path_t pathbuf; + vboxfs_crinfo_t crinfo; + int r; + + if ((r = vboxfs_set_path(&pathbuf, path)) != OK) + return r; + + memset(&crinfo, 0, sizeof(crinfo)); + crinfo.flags = VBOXFS_CRFLAG_LOOKUP; + /* crinfo.info.attr.add is not checked */ + + vbox_set_u32(¶m[0], vboxfs_root); + vbox_set_ptr(¶m[1], &pathbuf, vboxfs_get_path_size(&pathbuf), + VBOX_DIR_OUT); + vbox_set_ptr(¶m[2], &crinfo, sizeof(crinfo), VBOX_DIR_INOUT); + + r = vbox_call(vboxfs_conn, VBOXFS_CALL_CREATE, param, 3, NULL); + if (r != OK) + return r; + + switch (crinfo.result) { + case VBOXFS_PATH_NOT_FOUND: + /* This could also be ENOTDIR. See note in handle.c. */ + case VBOXFS_FILE_NOT_FOUND: + return ENOENT; + case VBOXFS_FILE_EXISTS: + break; /* success */ + default: + return EIO; /* should never happen */ + } + + vboxfs_get_attr(attr, &crinfo.info); + + return OK; +} + +/* + * Set file size. + */ +static int +set_size(char *path, u64_t size) +{ + vboxfs_objinfo_t info; + vboxfs_handle_t h; + int r; + + if ((r = vboxfs_open_file(path, O_WRONLY, S_IFREG, &h, NULL)) != OK) + return r; + + memset(&info, 0, sizeof(info)); + info.size = size; + + r = vboxfs_getset_info(h, VBOXFS_INFO_SET | VBOXFS_INFO_SIZE, &info, + sizeof(info)); + + vboxfs_close_file(h); + + return r; +} + +/* + * Set file attributes. + */ +int +vboxfs_setattr(char *path, struct sffs_attr *attr) +{ + vboxfs_objinfo_t info; + vboxfs_handle_t h; + int r; + + /* + * Setting the size of a path cannot be combined with other attribute + * modifications, because we cannot fail atomically. + */ + if (attr->a_mask & SFFS_ATTR_SIZE) { + assert(attr->a_mask == SFFS_ATTR_SIZE); + + return set_size(path, attr->a_size); + } + + /* + * By passing a pointer to an object information structure, we open the + * file for attribute manipulation. Note that this call will open the + * file as a regular file. This works on directories as well. + */ + if ((r = vboxfs_open_file(path, O_WRONLY, 0, &h, &info)) != OK) + return r; + + info.attr.add = VBOXFS_OBJATTR_ADD_NONE; + + /* Update the file's permissions if requested. */ + if (attr->a_mask & SFFS_ATTR_MODE) + info.attr.mode = + VBOXFS_SET_MODE(info.attr.mode & S_IFMT, attr->a_mode); + + /* + * Update various file times if requested. Not all changes may + * be honered. A zero time indicates no change. + */ + info.atime = (attr->a_mask & SFFS_ATTR_ATIME) ? + set_time(&attr->a_atime) : 0; + info.mtime = (attr->a_mask & SFFS_ATTR_MTIME) ? + set_time(&attr->a_ctime) : 0; + info.ctime = (attr->a_mask & SFFS_ATTR_CTIME) ? + set_time(&attr->a_ctime) : 0; + info.crtime = (attr->a_mask & SFFS_ATTR_CRTIME) ? + set_time(&attr->a_crtime) : 0; + + r = vboxfs_getset_info(h, VBOXFS_INFO_SET | VBOXFS_INFO_FILE, &info, + sizeof(info)); + + vboxfs_close_file(h); + + return r; +} diff --git a/lib/libvboxfs/dir.c b/lib/libvboxfs/dir.c new file mode 100644 index 000000000..00c7f3799 --- /dev/null +++ b/lib/libvboxfs/dir.c @@ -0,0 +1,245 @@ +/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */ + +#include "inc.h" + +/* + * Directories will generally be accessed sequentially, but there is no + * guarantee that this is actually the case. In particular, multiple user + * processes may iterate over the same directory concurrently, and since + * process information is lost in the VFS/FS protocol, the result is a + * nonsequential pattern on the same single directory handle. Therefore, we + * must support random access. Since the VirtualBox shared folders interface + * does not allow for random access (the resume point cannot be used for this), + * we choose to read in the contents of the directory upon open, and cache it + * until the directory is closed again. The risk is that the cached contents + * will go stale. + * + * The directory will always be closed once one reader finishes going through + * the entire directory, so the problem is rather limited anyway. Ideally, the + * directory contents would be refreshed upon any invalidating local action as + * well as after a certain time span (since the file system can be changed from + * the host as well). This is currently not implemented, because the odds of + * things going wrong are pretty small, and the effects are not devastating. + * + * The calling functions may also request the same directory entry twice in a + * row, because the entry does not fit in the user buffer the first time. The + * routines here optimize for repeat-sequential access, while supporting fully + * random access as well. + */ + +#define VBOXFS_DIRDATA_SIZE 8192 /* data allocation granularity */ + +typedef struct vboxfs_dirblock_s { + STAILQ_ENTRY(vboxfs_dirblock_s) next; + unsigned int count; + char data[VBOXFS_DIRDATA_SIZE]; +} vboxfs_dirblock_t; + +typedef struct { + STAILQ_HEAD(blocks, vboxfs_dirblock_s) blocks; + unsigned int index; + vboxfs_dirblock_t *block; + unsigned int bindex; + unsigned int bpos; +} vboxfs_dirdata_t; + +/* + * Free the memory allocated for the given directory contents storage. + */ +static void +free_dir(vboxfs_dirdata_t *dirdata) +{ + vboxfs_dirblock_t *block; + + while (!STAILQ_EMPTY(&dirdata->blocks)) { + block = STAILQ_FIRST(&dirdata->blocks); + + STAILQ_REMOVE_HEAD(&dirdata->blocks, next); + + free(block); + } + + free(dirdata); +} + +/* + * Read all the contents of the given directory, allocating memory as needed to + * store the data. + */ +static int +read_dir(vboxfs_handle_t handle, sffs_dir_t *dirp) +{ + vboxfs_dirdata_t *dirdata; + vboxfs_dirblock_t *block; + vbox_param_t param[8]; + unsigned int count; + int r; + + dirdata = (vboxfs_dirdata_t *) malloc(sizeof(vboxfs_dirdata_t)); + if (dirdata == NULL) + return ENOMEM; + + memset(dirdata, 0, sizeof(*dirdata)); + STAILQ_INIT(&dirdata->blocks); + + r = OK; + + do { + block = + (vboxfs_dirblock_t *) malloc(sizeof(vboxfs_dirblock_t)); + if (block == NULL) { + r = ENOMEM; + break; + } + + vbox_set_u32(¶m[0], vboxfs_root); + vbox_set_u64(¶m[1], handle); + vbox_set_u32(¶m[2], 0); /* flags */ + vbox_set_u32(¶m[3], sizeof(block->data)); + vbox_set_ptr(¶m[4], NULL, 0, VBOX_DIR_OUT); + vbox_set_ptr(¶m[5], block->data, sizeof(block->data), + VBOX_DIR_IN); + vbox_set_u32(¶m[6], 0); /* resume point */ + vbox_set_u32(¶m[7], 0); /* number of files */ + + /* If the call fails, stop. */ + if ((r = vbox_call(vboxfs_conn, VBOXFS_CALL_LIST, param, 8, + NULL)) != OK) { + free(block); + break; + } + + /* If the number of returned files is zero, stop. */ + if ((count = vbox_get_u32(¶m[7])) == 0) { + free(block); + break; + } + + /* + * Add the block to the list. We could realloc() the block to + * free unused tail space, but this is not likely to gain us + * much in general. + */ + block->count = count; + STAILQ_INSERT_TAIL(&dirdata->blocks, block, next); + + /* Continue until a zero resume point is returned. */ + } while (vbox_get_u32(¶m[6]) != 0); + + if (r != OK) { + free_dir(dirdata); + + return r; + } + + dirdata->block = STAILQ_FIRST(&dirdata->blocks); + + *dirp = (sffs_dir_t) dirdata; + + return OK; +} + +/* + * Open a directory. + */ +int +vboxfs_opendir(char *path, sffs_dir_t *handle) +{ + vboxfs_handle_t h; + int r; + + /* Open the directory. */ + if ((r = vboxfs_open_file(path, O_RDONLY, S_IFDIR, &h, NULL)) != OK) + return r; + + /* + * Upon success, read in the full contents of the directory right away. + * If it succeeds, this will also set the caller's directory handle. + */ + r = read_dir(h, handle); + + /* We do not need the directory to be open anymore now. */ + vboxfs_close_file(h); + + return r; +} + +/* + * Read one entry from a directory. On success, copy the name into buf, and + * return the requested attributes. If the name (including terminating null) + * exceeds size, return ENAMETOOLONG. Do not return dot and dot-dot entries. + * Return ENOENT if the index exceeds the number of files. + */ +int +vboxfs_readdir(sffs_dir_t handle, unsigned int index, char *buf, size_t size, + struct sffs_attr *attr) +{ + vboxfs_dirdata_t *dirdata; + vboxfs_dirinfo_t *dirinfo; + int r; + + dirdata = (vboxfs_dirdata_t *) handle; + + /* + * If the saved index exceeds the requested index, start from the + * beginning. + */ + if (dirdata->index > index) { + dirdata->index = 0; + dirdata->bindex = 0; + dirdata->bpos = 0; + dirdata->block = STAILQ_FIRST(&dirdata->blocks); + } + + /* Loop until we find the requested entry or we run out of entries. */ + while (dirdata->block != NULL) { + dirinfo = + (vboxfs_dirinfo_t *) &dirdata->block->data[dirdata->bpos]; + + /* Consider all entries that are not dot or dot-dot. */ + if (dirinfo->name.len > 2 || dirinfo->name.data[0] != '.' || + (dirinfo->name.len == 2 && dirinfo->name.data[1] != '.')) { + + if (dirdata->index == index) + break; + + dirdata->index++; + } + + /* Advance to the next entry. */ + dirdata->bpos += offsetof(vboxfs_dirinfo_t, name) + + offsetof(vboxfs_path_t, data) + dirinfo->name.size; + if (++dirdata->bindex >= dirdata->block->count) { + dirdata->block = STAILQ_NEXT(dirdata->block, next); + dirdata->bindex = 0; + dirdata->bpos = 0; + } + } + + /* Not enough files to satisfy the request? */ + if (dirdata->block == NULL) + return ENOENT; + + /* Return the information for the file we found. */ + if ((r = vboxfs_get_path(&dirinfo->name, buf, size)) != OK) + return r; + + vboxfs_get_attr(attr, &dirinfo->info); + + return OK; +} + +/* + * Close a directory. + */ +int +vboxfs_closedir(sffs_dir_t handle) +{ + vboxfs_dirdata_t *dirdata; + + dirdata = (vboxfs_dirdata_t *) handle; + + free_dir(dirdata); + + return OK; +} diff --git a/lib/libvboxfs/file.c b/lib/libvboxfs/file.c new file mode 100644 index 000000000..da96174f4 --- /dev/null +++ b/lib/libvboxfs/file.c @@ -0,0 +1,119 @@ +/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */ + +#include "inc.h" + +/* + * We perform all file I/O using a local, intermediate buffer. While in theory + * it would be possible to perform direct DMA from/to the user process, this + * does not work in practice: on short reads, VirtualBox copies back the entire + * provided buffer rather than only the part actually filled, resulting in the + * unused part of the buffer being clobbered. Marking the buffer as bi- + * directional would solve this, except it would also eliminate all the + * zero-copy benefits for reads; in addition, it is prevented by the protection + * set on the given grant. + */ + +#define VBOXFS_MAX_FILEIO 65536 /* maximum I/O chunk size */ + +static char iobuf[VBOXFS_MAX_FILEIO]; + +/* + * Open a file. + */ +int +vboxfs_open(char *path, int flags, int mode, sffs_file_t *handle) +{ + vboxfs_handle_t *handlep; + int r; + + handlep = (vboxfs_handle_t *) malloc(sizeof(*handlep)); + + if ((r = vboxfs_open_file(path, flags, mode, handlep, NULL)) != OK) { + free(handlep); + + return r; + } + + *handle = (sffs_file_t) handlep; + + return OK; +} + +/* + * Read or write a chunk from or to a file. + */ +static ssize_t +read_write(vboxfs_handle_t handle, char *buf, size_t size, u64_t pos, + int write) +{ + vbox_param_t param[5]; + int r, dir, call; + + dir = write ? VBOX_DIR_OUT : VBOX_DIR_IN; + call = write ? VBOXFS_CALL_WRITE : VBOXFS_CALL_READ; + + vbox_set_u32(¶m[0], vboxfs_root); + vbox_set_u64(¶m[1], handle); + vbox_set_u64(¶m[2], pos); + vbox_set_u32(¶m[3], size); + vbox_set_ptr(¶m[4], buf, size, dir); + + if ((r = vbox_call(vboxfs_conn, call, param, 5, NULL)) != OK) + return r; + + return vbox_get_u32(¶m[3]); +} + +/* + * Read from a file. + */ +ssize_t +vboxfs_read(sffs_file_t handle, char *buf, size_t size, u64_t pos) +{ + vboxfs_handle_t *handlep; + + handlep = (vboxfs_handle_t *) handle; + + return read_write(*handlep, buf, size, pos, FALSE /*write*/); +} + +/* + * Write to a file. + */ +ssize_t +vboxfs_write(sffs_file_t handle, char *buf, size_t len, u64_t pos) +{ + vboxfs_handle_t *handlep; + + handlep = (vboxfs_handle_t *) handle; + + return read_write(*handlep, buf, len, pos, TRUE /*write*/); +} + +/* + * Close a file handle. + */ +int +vboxfs_close(sffs_file_t handle) +{ + vboxfs_handle_t *handlep; + + handlep = (vboxfs_handle_t *) handle; + + vboxfs_close_file(*handlep); + + free(handlep); + + return OK; +} + +/* + * Return an internal buffer address and size for I/O operations. + */ +size_t +vboxfs_buffer(char **ptr) +{ + + *ptr = iobuf; + return sizeof(iobuf); +} diff --git a/lib/libvboxfs/glo.h b/lib/libvboxfs/glo.h new file mode 100644 index 000000000..b74131b90 --- /dev/null +++ b/lib/libvboxfs/glo.h @@ -0,0 +1,9 @@ +/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */ + +#ifndef _VBOXFS_GLO_H +#define _VBOXFS_GLO_H + +extern vbox_conn_t vboxfs_conn; +extern vboxfs_root_t vboxfs_root; + +#endif /* !_VBOXFS_GLO_H */ diff --git a/lib/libvboxfs/handle.c b/lib/libvboxfs/handle.c new file mode 100644 index 000000000..c264a8bee --- /dev/null +++ b/lib/libvboxfs/handle.c @@ -0,0 +1,121 @@ +/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */ + +#include "inc.h" + +/* + * Create or open a file or directory. + */ +int +vboxfs_open_file(char *path, int flags, int mode, vboxfs_handle_t *handlep, + vboxfs_objinfo_t *infop) +{ + vbox_param_t param[3]; + vboxfs_path_t pathbuf; + vboxfs_crinfo_t crinfo; + int r, dir, rflag, wflag; + + if ((r = vboxfs_set_path(&pathbuf, path)) != OK) + return r; + + memset(&crinfo, 0, sizeof(crinfo)); + + /* + * Note that the mode may not be set at all. If no new file may be + * created, this is not a problem. The following test succeeds only if + * the caller explicitly specified that a directory is involved. + */ + dir = S_ISDIR(mode); + + /* Convert open(2) flags to VirtualBox creation flags. */ + if (flags & O_APPEND) + return EINVAL; /* not supported at this time */ + + if (flags & O_CREAT) { + crinfo.flags = VBOXFS_CRFLAG_CREATE_IF_NEW; + + if (flags & O_EXCL) + crinfo.flags |= VBOXFS_CRFLAG_FAIL_IF_EXISTS; + else if (flags & O_TRUNC) + crinfo.flags |= VBOXFS_CRFLAG_TRUNC_IF_EXISTS; + else + crinfo.flags |= VBOXFS_CRFLAG_OPEN_IF_EXISTS; + } else { + crinfo.flags = VBOXFS_CRFLAG_FAIL_IF_NEW; + + if (flags & O_TRUNC) + crinfo.flags |= VBOXFS_CRFLAG_TRUNC_IF_EXISTS; + else + crinfo.flags |= VBOXFS_CRFLAG_OPEN_IF_EXISTS; + } + + /* + * If an object information structure is given, open the file only to + * retrieve or change its attributes. + */ + if (infop != NULL) { + rflag = VBOXFS_CRFLAG_READ_ATTR; + wflag = VBOXFS_CRFLAG_WRITE_ATTR; + } else { + rflag = VBOXFS_CRFLAG_READ; + wflag = VBOXFS_CRFLAG_WRITE; + } + + switch (flags & O_ACCMODE) { + case O_RDONLY: crinfo.flags |= rflag; break; + case O_WRONLY: crinfo.flags |= wflag; break; + case O_RDWR: crinfo.flags |= rflag | wflag; break; + default: return EINVAL; + } + + if (S_ISDIR(mode)) + crinfo.flags |= VBOXFS_CRFLAG_DIRECTORY; + + crinfo.info.attr.mode = VBOXFS_SET_MODE(dir ? S_IFDIR : S_IFREG, mode); + crinfo.info.attr.add = VBOXFS_OBJATTR_ADD_NONE; + + vbox_set_u32(¶m[0], vboxfs_root); + vbox_set_ptr(¶m[1], &pathbuf, vboxfs_get_path_size(&pathbuf), + VBOX_DIR_OUT); + vbox_set_ptr(¶m[2], &crinfo, sizeof(crinfo), VBOX_DIR_INOUT); + + r = vbox_call(vboxfs_conn, VBOXFS_CALL_CREATE, param, 3, NULL); + if (r != OK) + return r; + + if (crinfo.handle == VBOXFS_INVALID_HANDLE) { + switch (crinfo.result) { + case VBOXFS_PATH_NOT_FOUND: + /* + * This could also mean ENOTDIR, but there does not + * appear to be any way to distinguish that case. + * Verifying with extra lookups seems overkill. + */ + case VBOXFS_FILE_NOT_FOUND: + return ENOENT; + case VBOXFS_FILE_EXISTS: + return EEXIST; + default: + return EIO; /* should never happen */ + } + } + + *handlep = crinfo.handle; + if (infop != NULL) + *infop = crinfo.info; + return OK; +} + +/* + * Close an open file handle. + */ +void +vboxfs_close_file(vboxfs_handle_t handle) +{ + vbox_param_t param[2]; + + vbox_set_u32(¶m[0], vboxfs_root); + vbox_set_u64(¶m[1], handle); + + /* Ignore errors here. We cannot do anything with them anyway. */ + (void) vbox_call(vboxfs_conn, VBOXFS_CALL_CLOSE, param, 2, NULL); +} diff --git a/lib/libvboxfs/inc.h b/lib/libvboxfs/inc.h new file mode 100644 index 000000000..ba9313cb0 --- /dev/null +++ b/lib/libvboxfs/inc.h @@ -0,0 +1,18 @@ +/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */ + +#ifndef _VBOXFS_INC_H +#define _VBOXFS_INC_H + +#include +#include +#include +#include +#include +#include +#include + +#include "vboxfs.h" +#include "glo.h" +#include "proto.h" + +#endif /* !_VBOXFS_INC_H */ diff --git a/lib/libvboxfs/info.c b/lib/libvboxfs/info.c new file mode 100644 index 000000000..767744d71 --- /dev/null +++ b/lib/libvboxfs/info.c @@ -0,0 +1,58 @@ +/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */ + +#include "inc.h" + +/* + * Get or set file information. + */ +int +vboxfs_getset_info(vboxfs_handle_t handle, u32_t flags, void *data, + size_t size) +{ + vbox_param_t param[5]; + + vbox_set_u32(¶m[0], vboxfs_root); + vbox_set_u64(¶m[1], handle); + vbox_set_u32(¶m[2], flags); + vbox_set_u32(¶m[3], size); + vbox_set_ptr(¶m[4], data, size, VBOX_DIR_INOUT); + + return vbox_call(vboxfs_conn, VBOXFS_CALL_INFO, param, 5, NULL); +} + +/* + * Query volume information. + */ +int +vboxfs_query_vol(char *path, vboxfs_volinfo_t *volinfo) +{ + vboxfs_handle_t h; + int r; + + if ((r = vboxfs_open_file(path, O_RDONLY, 0, &h, NULL)) != OK) + return r; + + r = vboxfs_getset_info(h, VBOXFS_INFO_GET | VBOXFS_INFO_VOLUME, + volinfo, sizeof(*volinfo)); + + vboxfs_close_file(h); + + return r; +} + +/* + * Query volume information. + */ +int +vboxfs_queryvol(char *path, u64_t *free, u64_t *total) +{ + vboxfs_volinfo_t volinfo; + int r; + + if ((r = vboxfs_query_vol(path, &volinfo)) != OK) + return r; + + *free = volinfo.free; + *total = volinfo.total; + return OK; +} diff --git a/lib/libvboxfs/link.c b/lib/libvboxfs/link.c new file mode 100644 index 000000000..b525d3d6e --- /dev/null +++ b/lib/libvboxfs/link.c @@ -0,0 +1,109 @@ +/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */ + +#include "inc.h" + +/* + * Create a directory. + */ +int +vboxfs_mkdir(char *path, int mode) +{ + vboxfs_handle_t h; + int r; + + assert(S_ISDIR(mode)); + + if ((r = vboxfs_open_file(path, O_CREAT | O_EXCL | O_WRONLY, mode, &h, + NULL)) != OK) + return r; + + vboxfs_close_file(h); + + return r; +} + +/* + * Remove a file or directory. + */ +static int +remove_file(char *path, int dir) +{ + vbox_param_t param[3]; + vboxfs_path_t pathbuf; + int r; + + if ((r = vboxfs_set_path(&pathbuf, path)) != OK) + return r; + + /* FIXME: symbolic links are not supported at all yet */ + vbox_set_u32(¶m[0], vboxfs_root); + vbox_set_ptr(¶m[1], &pathbuf, vboxfs_get_path_size(&pathbuf), + VBOX_DIR_OUT); + vbox_set_u32(¶m[2], dir ? VBOXFS_REMOVE_DIR : VBOXFS_REMOVE_FILE); + + return vbox_call(vboxfs_conn, VBOXFS_CALL_REMOVE, param, 3, NULL); +} + +/* + * Unlink a file. + */ +int +vboxfs_unlink(char *path) +{ + + return remove_file(path, FALSE /*dir*/); +} + +/* + * Remove a directory. + */ +int +vboxfs_rmdir(char *path) +{ + + return remove_file(path, TRUE /*dir*/); +} + +/* + * Rename a file or directory. + */ +static int +rename_file(char *opath, char *npath, int dir) +{ + vbox_param_t param[4]; + vboxfs_path_t opathbuf, npathbuf; + u32_t flags; + int r; + + if ((r = vboxfs_set_path(&opathbuf, opath)) != OK) + return r; + + if ((r = vboxfs_set_path(&npathbuf, npath)) != OK) + return r; + + flags = dir ? VBOXFS_RENAME_DIR : VBOXFS_RENAME_FILE; + flags |= VBOXFS_RENAME_REPLACE; + + vbox_set_u32(¶m[0], vboxfs_root); + vbox_set_ptr(¶m[1], &opathbuf, vboxfs_get_path_size(&opathbuf), + VBOX_DIR_OUT); + vbox_set_ptr(¶m[2], &npathbuf, vboxfs_get_path_size(&npathbuf), + VBOX_DIR_OUT); + vbox_set_u32(¶m[3], flags); + + return vbox_call(vboxfs_conn, VBOXFS_CALL_RENAME, param, 4, NULL); +} + +/* + * Rename a file or directory. + */ +int +vboxfs_rename(char *opath, char *npath) +{ + int r; + + if ((r = rename_file(opath, npath, FALSE /*dir*/)) != EISDIR) + return r; + + return rename_file(opath, npath, TRUE /*dir*/); +} diff --git a/lib/libvboxfs/path.c b/lib/libvboxfs/path.c new file mode 100644 index 000000000..6b8c021d8 --- /dev/null +++ b/lib/libvboxfs/path.c @@ -0,0 +1,60 @@ +/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */ + +#include "inc.h" + +/* + * Store a local path name in the given path object, performing any necessary + * conversions. The path object is expected to be used read-only, so the size + * of the path object is set as small as possible. If 'name' is NULL, the path + * will be initialized to the empty string. + */ +int +vboxfs_set_path(vboxfs_path_t *path, char *name) +{ + size_t len; + + len = strlen(name); + + /* FIXME: missing UTF-8 conversion */ + + if (len >= sizeof(path->data)) + return ENAMETOOLONG; + + strcpy(path->data, name); + + path->len = len; + path->size = len + 1; + + return OK; +} + +/* + * Retrieve the path name from the given path object. Make sure the name fits + * in the given name buffer first. The given size must include room for a + * terminating null character. + */ +int +vboxfs_get_path(vboxfs_path_t *path, char *name, size_t size) +{ + + /* FIXME: missing UTF-8 conversion */ + + if (path->len >= size) + return ENAMETOOLONG; + + assert(path->data[path->len] == 0); + + strcpy(name, path->data); + + return OK; +} + +/* + * Return the byte size of a previously initialized path object. + */ +size_t +vboxfs_get_path_size(vboxfs_path_t *path) +{ + + return offsetof(vboxfs_path_t, data) + path->size; +} diff --git a/lib/libvboxfs/proto.h b/lib/libvboxfs/proto.h new file mode 100644 index 000000000..99cf74a4f --- /dev/null +++ b/lib/libvboxfs/proto.h @@ -0,0 +1,46 @@ +/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */ + +#ifndef _VBOXFS_PROTO_H +#define _VBOXFS_PROTO_H + +/* attr.c */ +void vboxfs_get_attr(struct sffs_attr *attr, vboxfs_objinfo_t *info); +int vboxfs_getattr(char *path, struct sffs_attr *attr); +int vboxfs_setattr(char *path, struct sffs_attr *attr); + +/* dir.c */ +int vboxfs_opendir(char *path, sffs_dir_t *handle); +int vboxfs_readdir(sffs_dir_t handle, unsigned int index, char *buf, + size_t size, struct sffs_attr *attr); +int vboxfs_closedir(sffs_dir_t handle); + +/* file.c */ +int vboxfs_open(char *path, int flags, int mode, sffs_file_t *handle); +ssize_t vboxfs_read(sffs_file_t handle, char *buf, size_t size, u64_t pos); +ssize_t vboxfs_write(sffs_file_t handle, char *buf, size_t len, u64_t pos); +int vboxfs_close(sffs_file_t handle); +size_t vboxfs_buffer(char **ptr); + +/* handle.c */ +int vboxfs_open_file(char *path, int flags, int mode, vboxfs_handle_t *handlep, + vboxfs_objinfo_t *infop); +void vboxfs_close_file(vboxfs_handle_t handle); + +/* info.c */ +int vboxfs_getset_info(vboxfs_handle_t handle, u32_t flags, void *data, + size_t size); +int vboxfs_query_vol(char *path, vboxfs_volinfo_t *volinfo); +int vboxfs_queryvol(char *path, u64_t *free, u64_t *total); + +/* link.c */ +int vboxfs_mkdir(char *path, int mode); +int vboxfs_unlink(char *path); +int vboxfs_rmdir(char *path); +int vboxfs_rename(char *opath, char *npath); + +/* path.c */ +int vboxfs_set_path(vboxfs_path_t *path, char *name); +int vboxfs_get_path(vboxfs_path_t *path, char *name, size_t size); +size_t vboxfs_get_path_size(vboxfs_path_t *path); + +#endif /* !_VBOXFS_PROTO_H */ diff --git a/lib/libvboxfs/vboxfs.c b/lib/libvboxfs/vboxfs.c new file mode 100644 index 000000000..28f60d680 --- /dev/null +++ b/lib/libvboxfs/vboxfs.c @@ -0,0 +1,98 @@ +/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */ + +#include "inc.h" + +vbox_conn_t vboxfs_conn; +vboxfs_root_t vboxfs_root; + +static struct sffs_table vboxfs_table = { + .t_open = vboxfs_open, + .t_read = vboxfs_read, + .t_write = vboxfs_write, + .t_close = vboxfs_close, + + .t_readbuf = vboxfs_buffer, + .t_writebuf = vboxfs_buffer, + + .t_opendir = vboxfs_opendir, + .t_readdir = vboxfs_readdir, + .t_closedir = vboxfs_closedir, + + .t_getattr = vboxfs_getattr, + .t_setattr = vboxfs_setattr, + + .t_mkdir = vboxfs_mkdir, + .t_unlink = vboxfs_unlink, + .t_rmdir = vboxfs_rmdir, + .t_rename = vboxfs_rename, + + .t_queryvol = vboxfs_queryvol +}; + +/* + * Initialize communication with the VBOX driver, and map the given share. + */ +int +vboxfs_init(char *share, const struct sffs_table **tablep, int *case_insens, + int *read_only) +{ + vbox_param_t param[4]; + vboxfs_path_t path; + vboxfs_volinfo_t volinfo; + int r; + + if ((r = vboxfs_set_path(&path, share)) != OK) + return r; + + if ((r = vbox_init()) != OK) + return r; + + if ((vboxfs_conn = r = vbox_open("VBoxSharedFolders")) < 0) + return r; + + r = vbox_call(vboxfs_conn, VBOXFS_CALL_SET_UTF8, NULL, 0, NULL); + if (r != OK) { + vbox_close(vboxfs_conn); + + return r; + } + + vbox_set_ptr(¶m[0], &path, vboxfs_get_path_size(&path), + VBOX_DIR_OUT); + vbox_set_u32(¶m[1], 0); + vbox_set_u32(¶m[2], '/'); /* path separator */ + vbox_set_u32(¶m[3], TRUE); /* case sensitivity - no effect? */ + + r = vbox_call(vboxfs_conn, VBOXFS_CALL_MAP_FOLDER, param, 4, NULL); + if (r != OK) { + vbox_close(vboxfs_conn); + + return r; + } + + vboxfs_root = vbox_get_u32(¶m[1]); + + /* Gather extra information about the mapped shared. */ + if (vboxfs_query_vol("", &volinfo) == OK) { + *case_insens = !volinfo.props.casesens; + *read_only = !!volinfo.props.readonly; + } + + *tablep = &vboxfs_table; + return OK; +} + +/* + * Unmap the share, and disconnect from the VBOX driver. + */ +void +vboxfs_cleanup(void) +{ + vbox_param_t param[1]; + + vbox_set_u32(¶m[0], vboxfs_root); + + vbox_call(vboxfs_conn, VBOXFS_CALL_UNMAP_FOLDER, param, 1, NULL); + + vbox_close(vboxfs_conn); +} diff --git a/lib/libvboxfs/vboxfs.h b/lib/libvboxfs/vboxfs.h new file mode 100644 index 000000000..79137c9a7 --- /dev/null +++ b/lib/libvboxfs/vboxfs.h @@ -0,0 +1,137 @@ +/* Part of libvboxfs - (c) 2012, D.C. van Moolenbroek */ + +#ifndef _VBOXFS_VBOXFS_H +#define _VBOXFS_VBOXFS_H + +#define VBOXFS_CALL_CREATE 3 /* create, open, lookup */ +#define VBOXFS_CALL_CLOSE 4 /* close handle */ +#define VBOXFS_CALL_READ 5 /* read from file */ +#define VBOXFS_CALL_WRITE 6 /* write to file */ +#define VBOXFS_CALL_LIST 8 /* list directory contents */ +#define VBOXFS_CALL_INFO 9 /* get/set file information */ +#define VBOXFS_CALL_REMOVE 11 /* remove file or directory */ +#define VBOXFS_CALL_UNMAP_FOLDER 13 /* unmap folder */ +#define VBOXFS_CALL_RENAME 14 /* rename file or directory */ +#define VBOXFS_CALL_SET_UTF8 16 /* switch to UTF8 */ +#define VBOXFS_CALL_MAP_FOLDER 17 /* map folder */ + +#define VBOXFS_INVALID_HANDLE ((vboxfs_handle_t) ~0LL) + +typedef u32_t vboxfs_root_t; +typedef u64_t vboxfs_handle_t; + +typedef struct { + u16_t size; + u16_t len; + char data[PATH_MAX]; +} vboxfs_path_t; + +#define VBOXFS_NO_RESULT 0 +#define VBOXFS_PATH_NOT_FOUND 1 +#define VBOXFS_FILE_NOT_FOUND 2 +#define VBOXFS_FILE_EXISTS 3 +#define VBOXFS_FILE_CREATED 4 +#define VBOXFS_FILE_REPLACED 5 + +#define VBOXFS_OBJATTR_ADD_NONE 1 /* no other attributes */ +#define VBOXFS_OBJATTR_ADD_UNIX 2 /* POSIX attributes */ +#define VBOXFS_OBJATTR_ADD_EATTR 3 /* extended attributes */ + +typedef struct { + u32_t mode; + u32_t add; + union { + struct { + u32_t uid; + u32_t gid; + u32_t nlinks; + u32_t dev; + u64_t inode; + u32_t flags; + u32_t gen; + u32_t rdev; + }; + struct { + u64_t easize; + }; + }; +} vboxfs_objattr_t; + +/* Thankfully, MINIX uses the universal UNIX mode values. */ +#define VBOXFS_GET_MODE(mode) ((mode) & 0xffff) +#define VBOXFS_SET_MODE(type, perm) ((type) | ((perm) & ALLPERMS)) + +typedef struct { + u64_t size; + u64_t disksize; + u64_t atime; + u64_t mtime; + u64_t ctime; + u64_t crtime; + vboxfs_objattr_t attr; +} vboxfs_objinfo_t; + +#define VBOXFS_CRFLAG_LOOKUP 0x00000001 +#define VBOXFS_CRFLAG_DIRECTORY 0x00000004 +#define VBOXFS_CRFLAG_OPEN_IF_EXISTS 0x00000000 +#define VBOXFS_CRFLAG_FAIL_IF_EXISTS 0x00000010 +#define VBOXFS_CRFLAG_REPLACE_IF_EXISTS 0x00000020 +#define VBOXFS_CRFLAG_TRUNC_IF_EXISTS 0x00000030 +#define VBOXFS_CRFLAG_CREATE_IF_NEW 0x00000000 +#define VBOXFS_CRFLAG_FAIL_IF_NEW 0x00000100 +#define VBOXFS_CRFLAG_READ 0x00001000 +#define VBOXFS_CRFLAG_WRITE 0x00002000 +#define VBOXFS_CRFLAG_APPEND 0x00004000 +#define VBOXFS_CRFLAG_READ_ATTR 0x00010000 +#define VBOXFS_CRFLAG_WRITE_ATTR 0x00020000 + +typedef struct { + vboxfs_handle_t handle; + u32_t result; + u32_t flags; + vboxfs_objinfo_t info; +} vboxfs_crinfo_t; + +typedef struct { + vboxfs_objinfo_t info; + u16_t shortlen; + u16_t shortname[14]; + vboxfs_path_t name; /* WARNING: name data size is dynamic! */ +} vboxfs_dirinfo_t; + +#define VBOXFS_INFO_GET 0x00 /* get file information */ +#define VBOXFS_INFO_SET 0x01 /* set file information */ + +#define VBOXFS_INFO_SIZE 0x04 /* get/set file size */ +#define VBOXFS_INFO_FILE 0x08 /* get/set file attributes */ +#define VBOXFS_INFO_VOLUME 0x10 /* get volume information */ + +#define VBOXFS_REMOVE_FILE 0x01 /* remove file */ +#define VBOXFS_REMOVE_DIR 0x02 /* remove directory */ +#define VBOXFS_REMOVE_SYMLINK 0x04 /* remove symbolic link */ + +#define VBOXFS_RENAME_FILE 0x01 /* rename file */ +#define VBOXFS_RENAME_DIR 0x02 /* rename directory */ +#define VBOXFS_RENAME_REPLACE 0x04 /* replace target if it exists */ + +typedef struct { + u32_t namemax; + u8_t remote; + u8_t casesens; + u8_t readonly; + u8_t unicode; + u8_t fscomp; + u8_t filecomp; + u16_t reserved; +} vboxfs_fsprops_t; + +typedef struct { + u64_t total; + u64_t free; + u32_t blocksize; + u32_t sectorsize; + u32_t serial; + vboxfs_fsprops_t props; +} vboxfs_volinfo_t; + +#endif /* !_VBOXFS_VBOXFS_H */ diff --git a/share/mk/bsd.prog.mk b/share/mk/bsd.prog.mk index 103849697..82c14f83f 100644 --- a/share/mk/bsd.prog.mk +++ b/share/mk/bsd.prog.mk @@ -59,7 +59,7 @@ MKDEP_SUFFIXES?= .o .ln # rumpfs_tmpfs rumpfs_udf rumpfs_ufs .for _lib in \ c curses blockdriver chardriver netdriver edit end m sys timers util \ - bz2 l audiodriver exec ddekit devman usb elf bdev sffs hgfs + bz2 l audiodriver exec ddekit devman usb elf bdev sffs hgfs vboxfs .ifndef LIB${_lib:tu} LIB${_lib:tu}= ${DESTDIR}/usr/lib/lib${_lib}.a .MADE: ${LIB${_lib:tu}} # Note: ${DESTDIR} will be expanded