HGFS - VMware Shared Folders file system server

This commit is contained in:
David van Moolenbroek 2010-01-25 23:18:02 +00:00
parent 4c2cb6c04f
commit 71fe2852f4
46 changed files with 4400 additions and 0 deletions

View file

@ -302,6 +302,22 @@ service isofs
uid 0;
};
service hgfs
{
system
TIMES # 25
GETINFO # 26
SAFECOPYFROM # 31
SAFECOPYTO # 32
SETGRANT # 34
PROFBUF # 38
SYSCTL # 44
;
ipc
SYSTEM PM VFS RS VM
;
};
service printer
{
io 378:4 # LPT1

76
man/man8/hgfs.8 Normal file
View file

@ -0,0 +1,76 @@
.TH HGFS 8
.SH NAME
hgfs \- Host/Guest File System server
.SH SYNOPSIS
\fBmount \-t hgfs \fR[\fB\-r\fR] [\fB\-o \fIoptions\fR] \fBnone \fImountpoint
.SH DESCRIPTION
The Host/Guest File System (HGFS) server allows one to mount
VMware Shared Folders as a file system. This makes it possible to access
selected portions of the VMware host file system when MINIX is run as a
VMware guest.
.PP
The above mount command will mount the hgfs file system onto the directory
\fImountpoint\fR. The \fB\-r\fR mount option makes the file system read-only;
note that shared folders may independently have been configured as read-only
on the VMware host. The \fIoptions\fR field is a string consisting of
comma-delimited \fIkey\fR or \fIkey\fB=\fIvalue\fR options. The following
options are supported.
.TP 4
\fBprefix=\fIpath\fR
This option sets a path prefix that will be prepended to all file system
operations on the host system. When mounted without a prefix (the default),
the root directory of an HGFS mount will contain all the names of the
available shares. The prefix option can be used to mount one of those shares,
by specifying its name as the prefix. Multi-component path prefixes are
supported as well.
.TP
\fBuid=\fInumber\fR
This sets the user ID used for all the files and directories in the file
system, allowing a non-root user to be the owner. The value must be specified
as a decimal number.
The default is root (the number \fB0\fR).
.TP
\fBgid=\fInumber\fR
Likewise, sets the group ID for all files and directories.
The default is operator (the number \fB0\fR).
.TP
\fBfmask=\fInumber\fR
This option sets the file permission mask of regular files. It is specified as
an octal number. For example, a value of \fB600\fR makes all files readable and
writable by the owning user (see the "\fBuid\fR" option).
The default is \fB755\fR.
.TP
\fBdmask=\fInumber\fR
Likewise, sets the file permission mask of directories.
The default is also \fB755\fR.
.TP
\fBicase\fR
This option tells HGFS to treat names as case-insensitive.
.TP
\fBnoicase\fR
This option, set by default, reverts the effect of an earlier specified
"\fBicase\fR" option.
.SH EXAMPLES
.TP 20
.B mount \-t hgfs none /mnt
# Mount the entire shared folders tree on \fI/mnt\fR
.TP 20
.B mount \-t hgfs \-o prefix=shared,uid=20,fmask=644,icase none /usr/shared
Mount the "\fIshared\fR" shared folder on \fI/usr/shared\fR
.SH LIMITATIONS
HGFS uses the first and original version of the VMware Shared Folders protocol
to talk to the VMware host. That means that HGFS should work with all VMware
products that support shared folders. However, this also imposes a number of
limitations. For example, the first version of the protocol supports only
regular files and directories (and not links), and does not have support for
automatic case sensitivity handling.
.PP
Some file system operations may not behave as expected, because the behavior
of HGFS is determined largely by the host. Other file system operations
(in particular, using directories as mountpoints) are not implemented,
because the file system structure as perceived by HGFS may change arbitrarily
at any time, due to modifications on the host side.
.SH "SEE ALSO"
.BR mount (1)
.SH AUTHOR
David van Moolenbroek <dcvmoole@cs.vu.nl>

View file

@ -20,6 +20,7 @@ all install depend clean:
cd ./mfs && $(MAKE) $@
cd ./pfs && $(MAKE) $@
cd ./iso9660fs && $(MAKE) $@
cd ./hgfs && $(MAKE) $@
cd ./rs && $(MAKE) $@
cd ./ds && $(MAKE) $@
cd ./is && $(MAKE) $@

34
servers/hgfs/Makefile Normal file
View file

@ -0,0 +1,34 @@
# Makefile for VMware Host/Guest File System (HGFS) server
SERVER=hgfs
LIBHGFSDIR=./libhgfs
LIBHGFS=$(LIBHGFSDIR)/libhgfs.a
DEST=/sbin/$(SERVER)
LIBS=-lsys -L$(LIBHGFSDIR) -lhgfs
OBJ=dentry.o handle.o inode.o link.o lookup.o main.o \
misc.o mount.o name.o optset.o path.o read.o \
stat.o table.o util.o verify.o write.o
all build: $(SERVER)
$(SERVER): $(LIBHGFS) $(OBJ)
$(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBS)
$(LIBHGFS):
cd $(LIBHGFSDIR) && $(MAKE)
install: $(SERVER)
install -c $(SERVER) $(DEST)
clean:
cd $(LIBHGFSDIR) && $(MAKE) $@
rm -f $(SERVER) $(OBJ)
depend:
cd $(LIBHGFSDIR) && $(MAKE) $@
mkdep "$(CC) -E $(CPPFLAGS)" *.c > .depend
# Include generated dependencies.
include .depend

13
servers/hgfs/const.h Normal file
View file

@ -0,0 +1,13 @@
/* Number of inodes. */
/* The following number must not exceed 16. The i_num field is only a short. */
#define NUM_INODE_BITS 8
#define NUM_INODES ((1 << NUM_INODE_BITS) - 1)
/* Number of entries in the name hashtable. */
#define NUM_HASH_SLOTS 1023
/* Arbitrary block size constant returned by fstatfs. du(1) uses this.
* Also used by getdents. This is not the actual HGFS data transfer unit size.
*/
#define BLOCK_SIZE 4096

194
servers/hgfs/dentry.c Normal file
View file

@ -0,0 +1,194 @@
/* This file contains directory entry management and the name lookup hashtable.
*
* The entry points into this file are:
* init_dentry initialize the directory entry name lookup hashtable
* lookup_dentry find an inode based on parent directory and name
* add_dentry add an inode as directory entry to a parent directory
* del_dentry delete an inode from its parent directory
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
PRIVATE LIST_HEAD(hash_head, inode) hash_table[NUM_HASH_SLOTS];
FORWARD _PROTOTYPE( void del_one_dentry, (struct inode *ino) );
FORWARD _PROTOTYPE( unsigned int hash_dentry, (struct inode *parent,
char *name) );
/*===========================================================================*
* init_dentry *
*===========================================================================*/
PUBLIC void init_dentry()
{
/* Initialize the names hashtable.
*/
int i;
for (i = 0; i < NUM_HASH_SLOTS; i++)
LIST_INIT(&hash_table[i]);
}
/*===========================================================================*
* lookup_dentry *
*===========================================================================*/
PUBLIC struct inode *lookup_dentry(parent, name)
struct inode *parent;
char *name;
{
/* Given a directory inode and a component name, look up the inode associated
* with that directory entry. Return the inode (with increased reference
* count) if found, or NIL_INODE otherwise.
*/
struct inode *ino;
unsigned int slot;
assert(IS_DIR(parent));
slot = hash_dentry(parent, name);
LIST_FOREACH(ino, &hash_table[slot], i_hash) {
if (compare_name(ino->i_name, name) == TRUE)
break;
}
if (ino == NIL_INODE)
return NIL_INODE;
get_inode(ino);
return ino;
}
/*===========================================================================*
* add_dentry *
*===========================================================================*/
PUBLIC void add_dentry(parent, name, ino)
struct inode *parent;
char *name;
struct inode *ino;
{
/* Add an entry to a parent inode, in the form of a new inode, with the given
* name. An entry with this name must not already exist.
*/
unsigned int slot;
assert(IS_DIR(parent));
assert(parent->i_ref > 0);
assert(ino->i_ref > 0);
assert(name[0]);
assert(strlen(name) <= NAME_MAX);
link_inode(parent, ino);
strcpy(ino->i_name, name);
/* hash_add(ino); */
slot = hash_dentry(parent, ino->i_name);
LIST_INSERT_HEAD(&hash_table[slot], ino, i_hash);
}
/*===========================================================================*
* del_one_dentry *
*===========================================================================*/
PRIVATE void del_one_dentry(ino)
struct inode *ino;
{
/* This inode has become inaccessible by name. Disassociate it from its parent
* and remove it from the names hash table.
*/
/* There can and must be exactly one root inode, so don't delete it! */
if (IS_ROOT(ino))
return;
/* INUSE -> DELETED, CACHED -> FREE */
/* Remove the entry from the hashtable.
* Decrease parent's refcount, possibly adding it to the free list.
* Do not touch open handles. Do not add to the free list.
*/
assert(ino->i_parent != NIL_INODE);
/* hash_del(ino); */
LIST_REMOVE(ino, i_hash);
ino->i_name[0] = 0;
unlink_inode(ino);
}
/*===========================================================================*
* del_dentry *
*===========================================================================*/
PUBLIC void del_dentry(ino)
struct inode *ino;
{
/* Disassociate an inode from its parent, effectively deleting it. Recursively
* delete all its children as well, fragmenting the deleted branch into single
* inodes.
*/
LIST_HEAD(work_list, inode) work_list;
struct inode *child;
del_one_dentry(ino);
/* Quick way out: one directory entry that itself has no children. */
if (!HAS_CHILDREN(ino))
return;
/* Recursively delete all children of the inode as well.
* Iterative version: this is potentially 128 levels deep.
*/
LIST_INIT(&work_list);
LIST_INSERT_HEAD(&work_list, ino, i_next);
do {
ino = LIST_FIRST(&work_list);
LIST_REMOVE(ino, i_next);
assert(IS_DIR(ino));
while (!LIST_EMPTY(&ino->i_child)) {
child = LIST_FIRST(&ino->i_child);
LIST_REMOVE(child, i_next);
del_one_dentry(child);
if (HAS_CHILDREN(child))
LIST_INSERT_HEAD(&work_list, child, i_next);
}
} while (!LIST_EMPTY(&work_list));
}
/*===========================================================================*
* hash_dentry *
*===========================================================================*/
PRIVATE unsigned int hash_dentry(parent, name)
struct inode *parent;
char *name;
{
/* Generate a hash value for a given name. Normalize the name first, so that
* different variations of the name will result in the same hash value.
*/
unsigned int val;
char buf[NAME_MAX+1], *p;
dprintf(("HGFS: hash_dentry for '%s'\n", name));
normalize_name(buf, name);
/* djb2 string hash algorithm, XOR variant */
val = 5381;
for (p = buf; *p; p++)
val = ((val << 5) + val) ^ *p;
/* Mix with inode number: typically, many file names occur in several
* different directories.
*/
return (val ^ parent->i_num) % NUM_HASH_SLOTS;
}

12
servers/hgfs/glo.h Normal file
View file

@ -0,0 +1,12 @@
#ifdef _TABLE
#undef EXTERN
#define EXTERN
#endif
EXTERN message m_in; /* request message */
EXTERN message m_out; /* reply message */
EXTERN struct state state; /* global state */
EXTERN struct opt opt; /* global options */
extern _PROTOTYPE( int (*call_vec[]), (void) );

78
servers/hgfs/handle.c Normal file
View file

@ -0,0 +1,78 @@
/* This file contains open file and directory handle management functions.
*
* The entry points into this file are:
* get_handle open a handle for an inode and store the handle
* put_handle close any handles associated with an inode
*
* Created:
* April 2007 (D.C. van Moolenbroek)
*/
#include "inc.h"
#include <fcntl.h>
/*===========================================================================*
* get_handle *
*===========================================================================*/
PUBLIC int get_handle(ino)
struct inode *ino;
{
/* Get an open file or directory handle for an inode.
*/
char path[PATH_MAX];
int r;
/* If we don't have a file handle yet, try to open the file now. */
if (ino->i_flags & I_HANDLE)
return OK;
if ((r = verify_inode(ino, path, NULL)) != OK)
return r;
if (IS_DIR(ino)) {
r = hgfs_opendir(path, &ino->i_dir);
}
else {
if (!state.read_only)
r = hgfs_open(path, O_RDWR, 0, &ino->i_file);
/* Protection or mount status might prevent us from writing. With the
* information that we have available, this is the best we can do..
*/
if (state.read_only || r != OK)
r = hgfs_open(path, O_RDONLY, 0, &ino->i_file);
}
if (r != OK)
return r;
ino->i_flags |= I_HANDLE;
return OK;
}
/*===========================================================================*
* put_handle *
*===========================================================================*/
PUBLIC void put_handle(ino)
struct inode *ino;
{
/* Close an open file or directory handle associated with an inode.
*/
int r;
if (!(ino->i_flags & I_HANDLE))
return;
/* We ignore any errors here, because we can't deal with them anyway. */
if (IS_DIR(ino))
r = hgfs_closedir(ino->i_dir);
else
r = hgfs_close(ino->i_file);
if (r != OK)
printf("HGFS: put_handle: handle close operation returned %d\n", r);
ino->i_flags &= ~I_HANDLE;
}

37
servers/hgfs/inc.h Normal file
View file

@ -0,0 +1,37 @@
#define _POSIX_SOURCE 1 /* for signal handling */
#define _SYSTEM 1 /* for negative error values */
#define _MINIX 1
#include <minix/config.h>
#include <minix/const.h>
#include <minix/type.h>
#include <minix/ipc.h>
#include <minix/com.h>
#include <minix/callnr.h>
#include <minix/safecopies.h>
#include <minix/vfsif.h>
#include <minix/syslib.h>
#include <minix/sysutil.h>
#if DEBUG
#define dprintf(x) printf x
#else
#define dprintf(x)
#endif
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/queue.h>
#include "libhgfs/hgfs.h"
#include "type.h"
#include "const.h"
#include "proto.h"
#include "glo.h"
#include "inode.h"

299
servers/hgfs/inode.c Normal file
View file

@ -0,0 +1,299 @@
/* This file deals with inode management.
*
* The entry points into this file are:
* init_inode initialize the inode table, return the root inode
* find_inode find an inode based on its inode number
* get_inode increase the reference count of an inode
* put_inode decrease the reference count of an inode
* link_inode link an inode as a directory entry to another inode
* unlink_inode unlink an inode from its parent directory
* get_free_inode return a free inode object
* have_free_inode check whether there is a free inode available
* have_used_inode check whether any inode is still in use
* do_putnode perform the PUTNODE file system call
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
PRIVATE TAILQ_HEAD(free_head, inode) free_list;
/*===========================================================================*
* init_inode *
*===========================================================================*/
PUBLIC struct inode *init_inode()
{
/* Initialize inode table. Return the root inode.
*/
struct inode *ino;
unsigned int index;
TAILQ_INIT(&free_list);
dprintf(("HGFS: %d inodes, %d bytes each, equals %d bytes\n",
NUM_INODES, sizeof(struct inode), sizeof(inodes)));
/* Mark all inodes except the root inode as free. */
for (index = 1; index < NUM_INODES; index++) {
ino = &inodes[index];
ino->i_parent = NIL_INODE;
LIST_INIT(&ino->i_child);
ino->i_num = index + 1;
ino->i_gen = (unsigned short)-1; /* aesthetics */
ino->i_ref = 0;
ino->i_flags = 0;
TAILQ_INSERT_TAIL(&free_list, ino, i_free);
}
/* Initialize and return the root inode. */
ino = &inodes[0];
ino->i_parent = ino; /* root inode is its own parent */
LIST_INIT(&ino->i_child);
ino->i_num = ROOT_INODE_NR;
ino->i_gen = 0; /* unused by root node */
ino->i_ref = 1; /* root inode is hereby in use */
ino->i_flags = I_DIR; /* root inode is a directory */
ino->i_name[0] = 0; /* root inode has empty name */
return ino;
}
/*===========================================================================*
* find_inode *
*===========================================================================*/
PUBLIC struct inode *find_inode(ino_nr)
ino_t ino_nr;
{
/* Get an inode based on its inode number. Do not increase its reference count.
*/
struct inode *ino;
unsigned int index;
/* Inode 0 (= index -1) is not a valid inode number. */
index = INODE_INDEX(ino_nr);
if (index < 0) {
printf("HGFS: VFS passed invalid inode number!\n");
return NIL_INODE;
}
assert(index < NUM_INODES);
ino = &inodes[index];
/* Make sure the generation number matches. */
if (INODE_GEN(ino_nr) != ino->i_gen) {
printf("HGFS: VFS passed outdated inode number!\n");
return NIL_INODE;
}
/* The VFS/FS protocol only uses referenced inodes. */
if (ino->i_ref == 0)
printf("HGFS: VFS passed unused inode!\n");
return ino;
}
/*===========================================================================*
* get_inode *
*===========================================================================*/
PUBLIC void get_inode(ino)
struct inode *ino;
{
/* Increase the given inode's reference count. If both reference and link
* count were zero before, remove the inode from the free list.
*/
dprintf(("HGFS: get_inode(%p) ['%s']\n", ino, ino->i_name));
/* (INUSE, CACHED) -> INUSE */
/* If this is the first reference, remove the node from the free list. */
if (ino->i_ref == 0 && !HAS_CHILDREN(ino))
TAILQ_REMOVE(&free_list, ino, i_free);
ino->i_ref++;
if (ino->i_ref == 0)
panic("HGFS", "inode reference count wrapped", NO_NUM);
}
/*===========================================================================*
* put_inode *
*===========================================================================*/
PUBLIC void put_inode(ino)
struct inode *ino;
{
/* Decrease an inode's reference count. If this count has reached zero, close
* the inode's file handle, if any. If both reference and link count have
* reached zero, mark the inode as cached or free.
*/
dprintf(("HGFS: put_inode(%p) ['%s']\n", ino, ino->i_name));
assert(ino != NIL_INODE);
assert(ino->i_ref > 0);
ino->i_ref--;
/* If there are still references to this inode, we're done here. */
if (ino->i_ref > 0)
return;
/* Close any file handle associated with this inode. */
put_handle(ino);
/* Only add the inode to the free list if there are also no links to it. */
if (HAS_CHILDREN(ino))
return;
/* INUSE -> CACHED, DELETED -> FREE */
/* Add the inode to the head or tail of the free list, depending on whether
* it is also deleted (and therefore can never be reused as is).
*/
if (ino->i_parent == NIL_INODE)
TAILQ_INSERT_HEAD(&free_list, ino, i_free);
else
TAILQ_INSERT_TAIL(&free_list, ino, i_free);
}
/*===========================================================================*
* link_inode *
*===========================================================================*/
PUBLIC void link_inode(parent, ino)
struct inode *parent;
struct inode *ino;
{
/* Link an inode to a parent. If both reference and link count were zero
* before, remove the inode from the free list. This function should only be
* called from add_dentry().
*/
/* This can never happen, right? */
if (parent->i_ref == 0 && !HAS_CHILDREN(parent))
TAILQ_REMOVE(&free_list, parent, i_free);
LIST_INSERT_HEAD(&parent->i_child, ino, i_next);
ino->i_parent = parent;
}
/*===========================================================================*
* unlink_inode *
*===========================================================================*/
PUBLIC void unlink_inode(ino)
struct inode *ino;
{
/* Unlink an inode from its parent. If both reference and link count have
* reached zero, mark the inode as cached or free. This function should only
* be used from del_dentry().
*/
struct inode *parent;
parent = ino->i_parent;
LIST_REMOVE(ino, i_next);
if (parent->i_ref == 0 && !HAS_CHILDREN(parent)) {
if (parent->i_parent == NIL_INODE)
TAILQ_INSERT_HEAD(&free_list, parent, i_free);
else
TAILQ_INSERT_TAIL(&free_list, parent, i_free);
}
ino->i_parent = NIL_INODE;
}
/*===========================================================================*
* get_free_inode *
*===========================================================================*/
PUBLIC struct inode *get_free_inode()
{
/* Return a free inode object (with reference count 1), if available.
*/
struct inode *ino;
/* [CACHED -> FREE,] FREE -> DELETED */
/* If there are no inodes on the free list, we cannot satisfy the request. */
if (TAILQ_EMPTY(&free_list)) {
printf("HGFS: out of inodes!\n");
return NIL_INODE;
}
ino = TAILQ_FIRST(&free_list);
TAILQ_REMOVE(&free_list, ino, i_free);
assert(ino->i_ref == 0);
assert(!HAS_CHILDREN(ino));
/* If this was a cached inode, free it first. */
if (ino->i_parent != NIL_INODE)
del_dentry(ino);
assert(ino->i_parent == NIL_INODE);
/* Initialize a subset of its fields */
ino->i_gen++;
ino->i_ref = 1;
return ino;
}
/*===========================================================================*
* have_free_inode *
*===========================================================================*/
PUBLIC int have_free_inode()
{
/* Check whether there are any free inodes at the moment. Kind of lame, but
* this allows for easier error recovery in some places.
*/
return !TAILQ_EMPTY(&free_list);
}
/*===========================================================================*
* have_used_inode *
*===========================================================================*/
PUBLIC int have_used_inode()
{
/* Check whether any inodes are still in use, that is, any of the inodes have
* a reference count larger than zero.
*/
unsigned int index;
for (index = 0; index < NUM_INODES; index++)
if (inodes[index].i_ref > 0)
return TRUE;
return FALSE;
}
/*===========================================================================*
* do_putnode *
*===========================================================================*/
PUBLIC int do_putnode()
{
/* Decrease an inode's reference count.
*/
struct inode *ino;
int count;
if ((ino = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
return EINVAL;
count = m_in.REQ_COUNT;
if (count <= 0 || count > ino->i_ref) return EINVAL;
ino->i_ref -= count - 1;
put_inode(ino);
return OK;
}

93
servers/hgfs/inode.h Normal file
View file

@ -0,0 +1,93 @@
#ifndef _INODE_H
#define _INODE_H
/* We cannot use inode number 0, so to be able to use bitmasks to combine
* inode and generation numbers, we have to use one fewer than the maximum of
* inodes possible by using NUM_INODE_BITS bits.
*/
#define NUM_INODES ((1 << NUM_INODE_BITS) - 1)
/* The main portion of the inode array forms a fully linked tree, providing a
* cached partial view of what the server believes is on the host system. Each
* inode contains only a pointer to its parent and its path component name, so
* a path for an inode is constructed by walking up to the root. Inodes that
* are in use as directory for a child node must not be recycled; in this case,
* the i_child list is not empty. Naturally, inodes for which VFS holds a
* reference must also not be recycled; the i_ref count takes care of that.
*
* Multiple hard links to a single file do not exist; that is why an inode is
* also a directory entry (when in IN USE or CACHED state). Notifications about
* modifications on the host system are not part of the protocol, so sometimes
* the server may discover that some files do not exist anymore. In that case,
* they are marked as DELETED in the inode table. Such files may still be used
* because of open file handles, but cannot be referenced by path anymore.
* Unfortunately the HGFS v1 protocol is largely path-oriented, so even
* truncating a deleted file is not possible. This has been fixed in v2/v3, but
* we currently use the v1 protocol for VMware backwards compatibility reasons.
*
* An inode is REFERENCED iff it has a reference count > 0 *or* has children.
* An inode is LINKED IN iff it has a parent.
*
* An inode is IN USE iff it is REFERENCED and LINKED IN.
* An inode is CACHED iff it is NOT REFERENCED and LINKED IN.
* An inode is DELETED iff it is REFERENCED and NOT LINKED IN.
* An inode is FREE iff it is NOT REFERENCED and NOT LINKED IN.
*
* An inode may have an open file handle if it is IN USE or DELETED.
* An inode may have children if it is IN USE (and is a directory).
* An inode is in the names hashtable iff it is IN USE or CACHED.
* An inode is on the free list iff it is CACHED or FREE.
*
* - An IN USE inode becomes DELETED when it is either deleted explicitly, or
* when it has been determined to have become unreachable by path name on the
* host system (the verify_* functions take care of this).
* - An IN USE inode may become CACHED when there are no VFS references to it
* anymore (i_ref == 0), and it is not a directory with children.
* - A DELETED inode cannot have children, but may become FREE when there are
* also no VFS references to it anymore.
* - A CACHED inode may become IN USE when either i_ref or i_link is increased
* from zero. Practically, it will always be i_ref that gets increased, since
* i_link cannot be increased by VFS without having a reference to the inode.
* - A CACHED or FREE inode may be reused for other purposes at any time.
*/
EXTERN struct inode {
struct inode *i_parent; /* parent inode pointer */
LIST_HEAD(child_head, inode) i_child; /* child inode anchor */
LIST_ENTRY(inode) i_next; /* sibling inode chain entry */
LIST_ENTRY(inode) i_hash; /* hashtable chain entry */
unsigned short i_num; /* inode number for quick reference */
unsigned short i_gen; /* inode generation number */
unsigned short i_ref; /* VFS reference count */
unsigned short i_flags; /* any combination of I_* flags */
union {
TAILQ_ENTRY(inode) u_free; /* free list chain entry */
hgfs_file_t u_file; /* handle to open HGFS file */
hgfs_dir_t u_dir; /* handle to open HGFS directory */
} i_u;
char i_name[NAME_MAX+1]; /* entry name in parent directory */
} inodes[NUM_INODES];
#define i_free i_u.u_free
#define i_file i_u.u_file
#define i_dir i_u.u_dir
#define NIL_INODE ((struct inode *)NULL)
#define I_DIR 0x01 /* this inode represents a directory */
#define I_HANDLE 0x02 /* this inode has an open handle */
/* warning: the following line is not a proper macro */
#define INODE_NR(i) (((i)->i_gen << NUM_INODE_BITS) | (i)->i_num)
#define INODE_INDEX(n) (((n) & ((1 << NUM_INODE_BITS) - 1)) - 1)
#define INODE_GEN(n) (((n) >> NUM_INODE_BITS) & 0xffff)
#define ROOT_INODE_NR 1
#define IS_DIR(i) ((i)->i_flags & I_DIR)
#define IS_ROOT(i) ((i)->i_num == ROOT_INODE_NR)
#define HAS_CHILDREN(i) (!LIST_EMPTY(&(i)->i_child))
#define MODE_TO_DIRFLAG(m) (S_ISDIR(m) ? I_DIR : 0)
#endif /* _INODE_H */

View file

@ -0,0 +1,26 @@
# Makefile for HGFS library
LIBNAME=libhgfs.a
OBJ=backdoor.o attr.o channel.o dir.o error.o file.o \
link.o misc.o path.o rpc.o time.o
AR=ar
GAS2ACK=gas2ack
all build: $(LIBNAME)
$(LIBNAME): $(OBJ)
$(AR) cr $@ $(OBJ)
backdoor.o: backdoor.S
$(GAS2ACK) $< $@.s
$(CC) $(CFLAGS) -c -o $@ $@.s
clean distclean:
rm -f $(LIBNAME) $(OBJ) *.o.s
depend:
mkdep "$(CC) -E $(CPPFLAGS)" *.c > .depend
# Include generated dependencies.
include .depend

View file

@ -0,0 +1,86 @@
/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
#include "inc.h"
#include <sys/stat.h>
/*===========================================================================*
* attr_get *
*===========================================================================*/
PUBLIC void attr_get(attr)
struct hgfs_attr *attr;
{
/* Get attribute information from the RPC buffer, storing the requested parts
* in the given attr structure.
*/
mode_t mode;
u32_t size_lo, size_hi;
mode = (RPC_NEXT32) ? S_IFDIR : S_IFREG;
size_lo = RPC_NEXT32;
size_hi = RPC_NEXT32;
if (attr->a_mask & HGFS_ATTR_SIZE)
attr->a_size = make64(size_lo, size_hi);
time_get((attr->a_mask & HGFS_ATTR_CRTIME) ? &attr->a_crtime : NULL);
time_get((attr->a_mask & HGFS_ATTR_ATIME) ? &attr->a_atime : NULL);
time_get((attr->a_mask & HGFS_ATTR_MTIME) ? &attr->a_mtime : NULL);
time_get((attr->a_mask & HGFS_ATTR_CTIME) ? &attr->a_ctime : NULL);
mode |= HGFS_PERM_TO_MODE(RPC_NEXT8);
if (attr->a_mask & HGFS_ATTR_MODE) attr->a_mode = mode;
}
/*===========================================================================*
* hgfs_getattr *
*===========================================================================*/
PUBLIC int hgfs_getattr(path, attr)
char *path;
struct hgfs_attr *attr;
{
/* Get selected attributes of a file by path name.
*/
int r;
RPC_REQUEST(HGFS_REQ_GETATTR);
path_put(path);
if ((r = rpc_query()) != OK)
return r;
attr_get(attr);
return OK;
}
/*===========================================================================*
* hgfs_setattr *
*===========================================================================*/
PUBLIC int hgfs_setattr(path, attr)
char *path;
struct hgfs_attr *attr;
{
/* Set selected attributes of a file by path name.
*/
RPC_REQUEST(HGFS_REQ_SETATTR);
RPC_NEXT8 = (attr->a_mask & HGFS_ATTR_ALL);
RPC_NEXT32 = !!(S_ISDIR(attr->a_mode));
RPC_NEXT32 = ex64lo(attr->a_size);
RPC_NEXT32 = ex64hi(attr->a_size);
time_put((attr->a_mask & HGFS_ATTR_CRTIME) ? &attr->a_crtime : NULL);
time_put((attr->a_mask & HGFS_ATTR_ATIME) ? &attr->a_atime : NULL);
time_put((attr->a_mask & HGFS_ATTR_MTIME) ? &attr->a_mtime : NULL);
time_put((attr->a_mask & HGFS_ATTR_CTIME) ? &attr->a_ctime : NULL);
RPC_NEXT8 = HGFS_MODE_TO_PERM(attr->a_mode);
path_put(path);
return rpc_query();
}

View file

@ -0,0 +1,106 @@
# Part of libhgfs - (c) 2009, D.C. van Moolenbroek
.globl __libhgfs_backdoor
.globl __libhgfs_backdoor_in
.globl __libhgfs_backdoor_out
.text
MAGIC = 0x564D5868
BD_PORT = 0x5658
IO_PORT = 0x5659
.balign 16
__libhgfs_backdoor:
pushl %ebx
pushl %esi
pushl %edi
pushl %ebp
movl 4+16(%esp), %ebp
movl $MAGIC, %eax
movl 4(%ebp), %ebx
movl 8(%ebp), %ecx
movl 12(%ebp), %edx
movw $BD_PORT, %dx
movl 16(%ebp), %esi
movl 20(%ebp), %edi
inl %dx
movl %eax, (%ebp)
movl %ebx, 4(%ebp)
movl %ecx, 8(%ebp)
movl %edx, 12(%ebp)
movl %esi, 16(%ebp)
movl %edi, 20(%ebp)
popl %ebp
popl %edi
popl %esi
popl %ebx
ret
.balign 16
__libhgfs_backdoor_in:
pushl %ebx
pushl %esi
pushl %edi
pushl %ebp
movl 4+16(%esp), %eax
movl 4(%eax), %ebx
movl 8(%eax), %ecx
movl 12(%eax), %edx
movw $IO_PORT, %dx
movl 16(%eax), %esi
movl 20(%eax), %edi
movl 24(%eax), %ebp
movl $MAGIC, %eax
cld
repe; insb
pushl %eax
movl 4+20(%esp), %eax
movl %ebx, 4(%eax)
movl %ecx, 8(%eax)
movl %edx, 12(%eax)
movl %esi, 16(%eax)
movl %edi, 20(%eax)
movl %ebp, 24(%eax)
popl %ebx
movl %ebx, (%eax)
movl (%eax), %eax
popl %ebp
popl %edi
popl %esi
popl %ebx
ret
.balign 16
__libhgfs_backdoor_out:
pushl %ebx
pushl %esi
pushl %edi
pushl %ebp
movl 4+16(%esp), %eax
movl 4(%eax), %ebx
movl 8(%eax), %ecx
movl 12(%eax), %edx
movw $IO_PORT, %dx
movl 16(%eax), %esi
movl 20(%eax), %edi
movl 24(%eax), %ebp
movl $MAGIC, %eax
cld
repe; outsb
pushl %eax
movl 4+20(%esp), %eax
movl %ebx, 4(%eax)
movl %ecx, 8(%eax)
movl %edx, 12(%eax)
movl %esi, 16(%eax)
movl %edi, 20(%eax)
movl %ebp, 24(%eax)
popl %ebx
movl %ebx, (%eax)
movl (%eax), %eax
popl %ebp
popl %edi
popl %esi
popl %ebx
ret

View file

@ -0,0 +1,146 @@
/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
#include "inc.h"
#define CMD_XFER 0x1E /* vmware backdoor transfer command */
enum {
XFER_OPEN, /* open transfer channel */
XFER_SENDLEN, /* specify length of data to send */
XFER_SEND, /* send data */
XFER_RECVLEN, /* get length of data to receive */
XFER_RECV, /* receive data */
XFER_RECVACK, /* acknowledge receipt of data */
XFER_CLOSE /* close transfer channel */
};
#define STATUS(p) (HIWORD((p)[2]) & 0xff)
/*===========================================================================*
* channel_open *
*===========================================================================*/
PUBLIC int channel_open(ch, type)
struct channel *ch; /* struct describing the new channel */
u32_t type; /* channel type: CH_IN or CH_OUT */
{
/* Open a new backdoor channel. Upon success, the given channel structure will
* be filled with information and can be used in subsequent channel calls.
* Return OK on success, or a negative error code on error.
*/
u32_t ptr[6];
ptr[1] = type | 0x80000000;
ptr[2] = MAKELONG(CMD_XFER, XFER_OPEN);
backdoor(ptr);
if ((STATUS(ptr) & 1) == 0) return EIO;
ch->id = HIWORD(ptr[3]);
ch->cookie1 = ptr[4];
ch->cookie2 = ptr[5];
return OK;
}
/*===========================================================================*
* channel_close *
*===========================================================================*/
PUBLIC void channel_close(ch)
struct channel *ch; /* backdoor channel to close */
{
/* Close a previously opened backdoor channel.
*/
u32_t ptr[6];
ptr[2] = MAKELONG(CMD_XFER, XFER_CLOSE);
ptr[3] = MAKELONG(0, ch->id);
ptr[4] = ch->cookie1;
ptr[5] = ch->cookie2;
backdoor(ptr);
}
/*===========================================================================*
* channel_send *
*===========================================================================*/
PUBLIC int channel_send(ch, buf, len)
struct channel *ch; /* backdoor channel to send to */
char *buf; /* buffer to send data from */
int len; /* size of the data to send */
{
/* Receive data over a backdoor channel. Return OK on success, or a negative
* error code on error.
*/
u32_t ptr[7];
ptr[1] = len;
ptr[2] = MAKELONG(CMD_XFER, XFER_SENDLEN);
ptr[3] = MAKELONG(0, ch->id);
ptr[4] = ch->cookie1;
ptr[5] = ch->cookie2;
backdoor(ptr);
if ((STATUS(ptr) & 1) == 0) return EIO;
if (len == 0) return OK;
ptr[1] = MAKELONG(0, 1);
ptr[2] = len;
ptr[3] = MAKELONG(0, ch->id);
ptr[4] = (u32_t)buf;
ptr[5] = ch->cookie2;
ptr[6] = ch->cookie1;
backdoor_out(ptr);
return OK;
}
/*===========================================================================*
* channel_recv *
*===========================================================================*/
PUBLIC int channel_recv(ch, buf, max)
struct channel *ch; /* backdoor channel to receive from */
char *buf; /* buffer to receive data into */
int max; /* size of the buffer */
{
/* Receive data on a backdoor channel. Return the number of bytes received, or
* a negative error code on error.
*/
u32_t ptr[7];
int len;
ptr[2] = MAKELONG(CMD_XFER, XFER_RECVLEN);
ptr[3] = MAKELONG(0, ch->id);
ptr[4] = ch->cookie1;
ptr[5] = ch->cookie2;
backdoor(ptr);
if ((STATUS(ptr) & 0x81) == 0) return EIO;
if ((len = ptr[1]) == 0 || (STATUS(ptr) & 3) == 1) return 0;
if (len > max) return E2BIG;
ptr[1] = MAKELONG(0, 1);
ptr[2] = len;
ptr[3] = MAKELONG(0, ch->id);
ptr[4] = ch->cookie1;
ptr[5] = (u32_t)buf;
ptr[6] = ch->cookie2;
backdoor_in(ptr);
ptr[1] = 1;
ptr[2] = MAKELONG(CMD_XFER, XFER_RECVACK);
ptr[3] = MAKELONG(0, ch->id);
ptr[4] = ch->cookie1;
ptr[5] = ch->cookie2;
backdoor(ptr);
return len;
}

View file

@ -0,0 +1,62 @@
/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
/* Various macros used here and there */
#define MAKELONG(a,b) ((a) | ((b) << 16))
#define HIWORD(d) ((d) >> 16)
#define LOWORD(d) ((d) & 0xffff)
#define BYTES(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
/* Valid channel types for channel_open() */
#define CH_IN BYTES('T', 'C', 'L', 'O')
#define CH_OUT BYTES('R', 'P', 'C', 'I')
/* RPC constants */
#define RPC_BUF_SIZE 6134 /* max size of RPC request */
#define RPC_HDR_SIZE 10 /* RPC HGFS header size */
/* RPC macros. These NEED NOT be portable. VMware only does x86(-64) anyway. */
/* ..all this because ACK can't pack structures :( */
#define RPC_NEXT8 *(((u8_t*)(++rpc_ptr))-1) /* get/set next byte */
#define RPC_NEXT16 *(((u16_t*)(rpc_ptr+=2))-1) /* get/set next short */
#define RPC_NEXT32 *(((u32_t*)(rpc_ptr+=4))-1) /* get/set next long */
#define RPC_LEN (rpc_ptr - rpc_buf) /* request length thus far */
#define RPC_ADVANCE(n) rpc_ptr += n /* skip n bytes in buffer */
#define RPC_PTR (rpc_ptr) /* pointer to next data */
#define RPC_RESET rpc_ptr = rpc_buf /* start at beginning */
#define RPC_REQUEST(r) \
RPC_RESET; \
RPC_NEXT8 = 'f'; \
RPC_NEXT8 = ' '; \
RPC_NEXT32 = 0; \
RPC_NEXT32 = r; /* start a RPC request */
/* HGFS requests */
enum {
HGFS_REQ_OPEN,
HGFS_REQ_READ,
HGFS_REQ_WRITE,
HGFS_REQ_CLOSE,
HGFS_REQ_OPENDIR,
HGFS_REQ_READDIR,
HGFS_REQ_CLOSEDIR,
HGFS_REQ_GETATTR,
HGFS_REQ_SETATTR,
HGFS_REQ_MKDIR,
HGFS_REQ_UNLINK,
HGFS_REQ_RMDIR,
HGFS_REQ_RENAME,
HGFS_REQ_QUERYVOL
};
/* HGFS open types */
enum {
HGFS_OPEN_TYPE_O,
HGFS_OPEN_TYPE_OT,
HGFS_OPEN_TYPE_CO,
HGFS_OPEN_TYPE_C,
HGFS_OPEN_TYPE_COT
};
/* HGFS mode/perms conversion macros */
#define HGFS_MODE_TO_PERM(m) (((m) & S_IRWXU) >> 6)
#define HGFS_PERM_TO_MODE(p) (((p) << 6) & S_IRWXU)

View file

@ -0,0 +1,71 @@
/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
#include "inc.h"
/*===========================================================================*
* hgfs_opendir *
*===========================================================================*/
PUBLIC int hgfs_opendir(path, handle)
char *path;
hgfs_dir_t *handle;
{
/* Open a directory. Store a directory handle upon success.
*/
int r;
RPC_REQUEST(HGFS_REQ_OPENDIR);
path_put(path);
if ((r = rpc_query()) != OK)
return r;
*handle = (hgfs_dir_t)RPC_NEXT32;
return OK;
}
/*===========================================================================*
* hgfs_readdir *
*===========================================================================*/
PUBLIC int hgfs_readdir(handle, index, buf, size, attr)
hgfs_dir_t handle;
unsigned int index;
char *buf;
size_t size;
struct hgfs_attr *attr;
{
/* Read a directory entry from an open directory, using a zero-based index
* number. Upon success, the resulting path name is stored in the given buffer
* and the given attribute structure is filled selectively as requested. Upon
* error, the contents of the path buffer and attribute structure are
* undefined.
*/
int r;
RPC_REQUEST(HGFS_REQ_READDIR);
RPC_NEXT32 = (u32_t)handle;
RPC_NEXT32 = index;
if ((r = rpc_query()) != OK)
return r;
attr_get(attr);
return path_get(buf, size);
}
/*===========================================================================*
* hgfs_closedir *
*===========================================================================*/
PUBLIC int hgfs_closedir(handle)
hgfs_dir_t handle;
{
/* Close an open directory.
*/
RPC_REQUEST(HGFS_REQ_CLOSEDIR);
RPC_NEXT32 = (u32_t)handle;
return rpc_query();
}

View file

@ -0,0 +1,40 @@
/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
#include "inc.h"
/* Not sure if all of these occur in the HGFS v1 protocol, but at least some
* that weren't in the original protocol, are being returned now.
*/
#define NERRS 16
PRIVATE int error_map[NERRS] = {
OK, /* no error */
ENOENT, /* no such file/directory */
EBADF, /* invalid handle */
EPERM, /* operation not permitted */
EEXIST, /* file already exists */
ENOTDIR, /* not a directory */
ENOTEMPTY, /* directory not empty */
EIO, /* protocol error */
EACCES, /* access denied */
EINVAL, /* invalid name */
EIO, /* generic error */
EIO, /* sharing violation */
ENOSPC, /* no space */
ENOSYS, /* operation not supported */
ENAMETOOLONG, /* name too long */
EINVAL, /* invalid parameter */
};
/*===========================================================================*
* error_convert *
*===========================================================================*/
PUBLIC int error_convert(err)
int err;
{
/* Convert a HGFS error into an errno error code.
*/
if (err < 0 || err >= NERRS) return EIO;
return error_map[err];
}

182
servers/hgfs/libhgfs/file.c Normal file
View file

@ -0,0 +1,182 @@
/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
#include "inc.h"
#include <fcntl.h>
#include <sys/stat.h>
/*===========================================================================*
* hgfs_open *
*===========================================================================*/
PUBLIC int hgfs_open(path, flags, mode, handle)
char *path; /* path name to open */
int flags; /* open flags to use */
int mode; /* mode to create (user bits only) */
hgfs_file_t *handle; /* place to store resulting handle */
{
/* Open a file. Store a file handle upon success.
*/
int r, type;
/* We could implement this, but that means we would have to start tracking
* open files in order to associate data with them. Rather not.
*/
if (flags & O_APPEND) return EINVAL;
if (flags & O_CREAT) {
if (flags & O_EXCL) type = HGFS_OPEN_TYPE_C;
else if (flags & O_TRUNC) type = HGFS_OPEN_TYPE_COT;
else type = HGFS_OPEN_TYPE_CO;
} else {
if (flags & O_TRUNC) type = HGFS_OPEN_TYPE_OT;
else type = HGFS_OPEN_TYPE_O;
}
RPC_REQUEST(HGFS_REQ_OPEN);
RPC_NEXT32 = (flags & O_ACCMODE);
RPC_NEXT32 = type;
RPC_NEXT8 = HGFS_MODE_TO_PERM(mode);
path_put(path);
if ((r = rpc_query()) != OK)
return r;
*handle = (hgfs_file_t)RPC_NEXT32;
return OK;
}
/*===========================================================================*
* hgfs_read *
*===========================================================================*/
PUBLIC int hgfs_read(handle, buf, size, off)
hgfs_file_t handle; /* handle to open file */
char *buf; /* data buffer or NULL */
size_t size; /* maximum number of bytes to read */
u64_t off; /* file offset */
{
/* Read from an open file. Upon success, return the number of bytes read.
*/
int r, len, max;
RPC_REQUEST(HGFS_REQ_READ);
RPC_NEXT32 = (u32_t)handle;
RPC_NEXT32 = ex64lo(off);
RPC_NEXT32 = ex64hi(off);
max = RPC_BUF_SIZE - RPC_LEN - sizeof(u32_t);
RPC_NEXT32 = (size < max) ? size : max;
if ((r = rpc_query()) != OK)
return r;
len = RPC_NEXT32;
if (len > max) len = max; /* sanity check */
/* Only copy out data if we're requested to do so. */
if (buf != NULL)
memcpy(buf, RPC_PTR, len);
return len;
}
/*===========================================================================*
* hgfs_write *
*===========================================================================*/
PUBLIC int hgfs_write(handle, buf, len, off, append)
hgfs_file_t handle; /* handle to open file */
const char *buf; /* data buffer or NULL */
size_t len; /* number of bytes to write */
u64_t off; /* file offset */
int append; /* if set, append to file (ignore offset) */
{
/* Write to an open file. Upon success, return the number of bytes written.
*/
int r;
RPC_REQUEST(HGFS_REQ_WRITE);
RPC_NEXT32 = (u32_t)handle;
if (append) {
RPC_NEXT8 = 1;
RPC_NEXT32 = 0;
RPC_NEXT32 = 0;
}
else {
RPC_NEXT8 = 0;
RPC_NEXT32 = ex64lo(off);
RPC_NEXT32 = ex64hi(off);
}
RPC_NEXT32 = len;
/* Only copy in data if we're requested to do so. */
if (buf != NULL)
memcpy(RPC_PTR, buf, len);
RPC_ADVANCE(len);
if ((r = rpc_query()) != OK)
return r;
return RPC_NEXT32;
}
/*===========================================================================*
* hgfs_close *
*===========================================================================*/
PUBLIC int hgfs_close(handle)
hgfs_file_t handle; /* handle to open file */
{
/* Close an open file.
*/
RPC_REQUEST(HGFS_REQ_CLOSE);
RPC_NEXT32 = (u32_t)handle;
return rpc_query();
}
/*===========================================================================*
* hgfs_readbuf *
*===========================================================================*/
PUBLIC size_t hgfs_readbuf(ptr)
char **ptr;
{
/* Return information about the read buffer, for zero-copy purposes. Store a
* pointer to the first byte of the read buffer, and return the maximum data
* size. The results are static, but must only be used directly prior to a
* hgfs_read() call (with a NULL data buffer address).
*/
u32_t off;
off = RPC_HDR_SIZE + sizeof(u32_t);
RPC_RESET;
RPC_ADVANCE(off);
*ptr = RPC_PTR;
return RPC_BUF_SIZE - off;
}
/*===========================================================================*
* hgfs_writebuf *
*===========================================================================*/
PUBLIC size_t hgfs_writebuf(ptr)
char **ptr;
{
/* Return information about the write buffer, for zero-copy purposes. Store a
* pointer to the first byte of the write buffer, and return the maximum data
* size. The results are static, but must only be used immediately after a
* hgfs_write() call (with a NULL data buffer address).
*/
u32_t off;
off = RPC_HDR_SIZE + sizeof(u32_t) + sizeof(u8_t) + sizeof(u32_t) * 3;
RPC_RESET;
RPC_ADVANCE(off);
*ptr = RPC_PTR;
return RPC_BUF_SIZE - off;
}

View file

@ -0,0 +1,6 @@
/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
#define rpc_buf PREFIX(rpc_buf)
#define rpc_ptr PREFIX(rpc_ptr)
extern char rpc_buf[RPC_BUF_SIZE];
extern char *rpc_ptr;

View file

@ -0,0 +1,63 @@
/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
#ifndef _HGFS_H
#define _HGFS_H
#include <sys/types.h>
#include <minix/u64.h>
typedef void *hgfs_file_t; /* handle to open file */
typedef void *hgfs_dir_t; /* handle to directory search */
struct hgfs_attr {
u32_t a_mask; /* which fields to retrieve/set */
mode_t a_mode; /* file type and permissions */
u64_t a_size; /* file size */
time_t a_crtime; /* file creation time */
time_t a_atime; /* file access time */
time_t a_mtime; /* file modification time */
time_t a_ctime; /* file change time */
};
#define HGFS_ATTR_SIZE 0x01 /* get/set file size */
#define HGFS_ATTR_CRTIME 0x02 /* get/set file creation time */
#define HGFS_ATTR_ATIME 0x04 /* get/set file access time */
#define HGFS_ATTR_MTIME 0x08 /* get/set file modification time */
#define HGFS_ATTR_CTIME 0x10 /* get/set file change time */
#define HGFS_ATTR_MODE 0x20 /* get/set file mode */
#define HGFS_ATTR_ALL \
(HGFS_ATTR_SIZE | HGFS_ATTR_CRTIME | HGFS_ATTR_ATIME | \
HGFS_ATTR_MTIME | HGFS_ATTR_CTIME | HGFS_ATTR_MODE)
_PROTOTYPE( int hgfs_init, (void) );
_PROTOTYPE( void hgfs_cleanup, (void) );
_PROTOTYPE( int hgfs_enabled, (void) );
_PROTOTYPE( int hgfs_open, (char *path, int flags, int mode,
hgfs_file_t *handle) );
_PROTOTYPE( int hgfs_read, (hgfs_file_t handle, char *buf, size_t size,
u64_t offset) );
_PROTOTYPE( int hgfs_write, (hgfs_file_t handle, const char *buf,
size_t len, u64_t offset, int append) );
_PROTOTYPE( int hgfs_close, (hgfs_file_t handle) );
_PROTOTYPE( size_t hgfs_readbuf, (char **ptr) );
_PROTOTYPE( size_t hgfs_writebuf, (char **ptr) );
_PROTOTYPE( int hgfs_opendir, (char *path, hgfs_dir_t *handle) );
_PROTOTYPE( int hgfs_readdir, (hgfs_dir_t handle, unsigned int index,
char *buf, size_t size, struct hgfs_attr *attr) );
_PROTOTYPE( int hgfs_closedir, (hgfs_dir_t handle) );
_PROTOTYPE( int hgfs_getattr, (char *path, struct hgfs_attr *attr) );
_PROTOTYPE( int hgfs_setattr, (char *path, struct hgfs_attr *attr) );
_PROTOTYPE( int hgfs_mkdir, (char *path, int mode) );
_PROTOTYPE( int hgfs_unlink, (char *path) );
_PROTOTYPE( int hgfs_rmdir, (char *path) );
_PROTOTYPE( int hgfs_rename, (char *opath, char *npath) );
_PROTOTYPE( int hgfs_queryvol, (char *path, u64_t *free, u64_t *total) );
#endif /* _HGFS_H */

View file

@ -0,0 +1,19 @@
/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
#define _POSIX_SOURCE 1 /* need PATH_MAX */
#define _SYSTEM 1 /* need negative error codes */
#include <minix/config.h>
#include <minix/const.h>
#include <string.h>
#include <errno.h>
#include "hgfs.h"
#define PREFIX(x) __libhgfs_##x
#include "type.h"
#include "const.h"
#include "proto.h"
#include "glo.h"

View file

@ -0,0 +1,73 @@
/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
#include "inc.h"
#include <sys/stat.h>
/*===========================================================================*
* hgfs_mkdir *
*===========================================================================*/
PUBLIC int hgfs_mkdir(path, mode)
char *path;
int mode;
{
/* Create a new directory.
*/
RPC_REQUEST(HGFS_REQ_MKDIR);
RPC_NEXT8 = HGFS_MODE_TO_PERM(mode);
path_put(path);
return rpc_query();
}
/*===========================================================================*
* hgfs_unlink *
*===========================================================================*/
PUBLIC int hgfs_unlink(path)
char *path;
{
/* Delete a file.
*/
RPC_REQUEST(HGFS_REQ_UNLINK);
path_put(path);
return rpc_query();
}
/*===========================================================================*
* hgfs_rmdir *
*===========================================================================*/
PUBLIC int hgfs_rmdir(path)
char *path;
{
/* Remove an empty directory.
*/
RPC_REQUEST(HGFS_REQ_RMDIR);
path_put(path);
return rpc_query();
}
/*===========================================================================*
* hgfs_rename *
*===========================================================================*/
PUBLIC int hgfs_rename(opath, npath)
char *opath;
char *npath;
{
/* Rename a file or directory.
*/
RPC_REQUEST(HGFS_REQ_RENAME);
path_put(opath);
path_put(npath);
return rpc_query();
}

View file

@ -0,0 +1,73 @@
/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
#include "inc.h"
/*===========================================================================*
* hgfs_init *
*===========================================================================*/
PUBLIC int hgfs_init()
{
/* Initialize the library. Return OK on success, or a negative error code
* otherwise. If EAGAIN is returned, shared folders are disabled; in that
* case, other operations may be tried (and possibly succeed).
*/
time_init();
return rpc_open();
}
/*===========================================================================*
* hgfs_cleanup *
*===========================================================================*/
PUBLIC void hgfs_cleanup()
{
/* Clean up state.
*/
rpc_close();
}
/*===========================================================================*
* hgfs_enabled *
*===========================================================================*/
PUBLIC int hgfs_enabled()
{
/* Check if shared folders are enabled. Return OK if so, EAGAIN if not, and
* another negative error code on error.
*/
return rpc_test();
}
/*===========================================================================*
* hgfs_queryvol *
*===========================================================================*/
PUBLIC int hgfs_queryvol(path, free, total)
char *path;
u64_t *free;
u64_t *total;
{
/* Retrieve information about available and total volume space associated with
* a given path.
*/
u32_t lo, hi;
int r;
RPC_REQUEST(HGFS_REQ_QUERYVOL);
path_put(path);
if ((r = rpc_query()) != OK)
return r;
lo = RPC_NEXT32;
hi = RPC_NEXT32;
*free = make64(lo, hi);
lo = RPC_NEXT32;
hi = RPC_NEXT32;
*total = make64(lo, hi);
return OK;
}

View file

@ -0,0 +1,73 @@
/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
#include "inc.h"
#include <limits.h>
/*===========================================================================*
* path_put *
*===========================================================================*/
PUBLIC void path_put(path)
char *path;
{
/* Append the given path name in HGFS format to the RPC buffer. Truncate it
* if it is longer than PATH_MAX bytes.
*/
char *p, buf[PATH_MAX];
int len;
/* No leading slashes are allowed. */
for (p = path; *p == '/'; p++);
/* No double or tailing slashes, either. */
for (len = 0; *p && len < sizeof(buf) - 1; len++) {
if (*p == '/') {
for (p++; *p == '/'; p++);
if (!*p) break;
buf[len] = 0;
}
else buf[len] = *p++;
}
RPC_NEXT32 = len;
memcpy(RPC_PTR, buf, len);
RPC_ADVANCE(len);
RPC_NEXT8 = 0;
}
/*===========================================================================*
* path_get *
*===========================================================================*/
PUBLIC int path_get(path, max)
char *path;
int max;
{
/* Retrieve a HGFS formatted path name from the RPC buffer. Returns EINVAL if
* the path name is invalid. Returns ENAMETOOLONG if the path name is too
* long. Returns OK on success.
*/
char *p, *q;
int n, len;
n = len = RPC_NEXT32;
if (len >= max) return ENAMETOOLONG;
for (p = path, q = RPC_PTR; n--; p++, q++) {
/* We can not deal with a slash in a path component. */
if (*q == '/') return EINVAL;
if (*q == 0) *p = '/';
else *p = *q;
}
RPC_ADVANCE(len);
*p = 0;
return (RPC_NEXT8 != 0) ? EINVAL : OK;
}

View file

@ -0,0 +1,51 @@
/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
/* attr.c */
#define attr_get PREFIX(attr_get)
_PROTOTYPE( void attr_get, (struct hgfs_attr *attr) );
/* backdoor.s */
#define backdoor PREFIX(backdoor)
#define backdoor_in PREFIX(backdoor_in)
#define backdoor_out PREFIX(backdoor_out)
_PROTOTYPE( u32_t backdoor, (u32_t ptr[6]) );
_PROTOTYPE( u32_t backdoor_in, (u32_t ptr[6]) );
_PROTOTYPE( u32_t backdoor_out, (u32_t ptr[6]) );
/* channel.c */
#define channel_open PREFIX(channel_open)
#define channel_close PREFIX(channel_close)
#define channel_send PREFIX(channel_send)
#define channel_recv PREFIX(channel_recv)
_PROTOTYPE( int channel_open, (struct channel *ch, u32_t type) );
_PROTOTYPE( void channel_close, (struct channel *ch) );
_PROTOTYPE( int channel_send, (struct channel *ch, char *buf, int len) );
_PROTOTYPE( int channel_recv, (struct channel *ch, char *buf, int max) );
/* error.c */
#define error_convert PREFIX(error_convert)
_PROTOTYPE( int error_convert, (int err) );
/* path.c */
#define path_put PREFIX(path_put)
#define path_get PREFIX(path_get)
_PROTOTYPE( void path_put, (char *path) );
_PROTOTYPE( int path_get, (char *path, int max) );
/* rpc.c */
#define rpc_open PREFIX(rpc_open)
#define rpc_query PREFIX(rpc_query)
#define rpc_test PREFIX(rpc_test)
#define rpc_close PREFIX(rpc_close)
_PROTOTYPE( int rpc_open, (void) );
_PROTOTYPE( int rpc_query, (void) );
_PROTOTYPE( int rpc_test, (void) );
_PROTOTYPE( void rpc_close, (void) );
/* time.c */
#define time_init PREFIX(time_init)
#define time_put PREFIX(time_put)
#define time_get PREFIX(time_get)
_PROTOTYPE( void time_init, (void) );
_PROTOTYPE( void time_put, (time_t *timep) );
_PROTOTYPE( void time_get, (time_t *timep) );

View file

@ -0,0 +1,94 @@
/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
#include "inc.h"
PUBLIC char rpc_buf[RPC_BUF_SIZE];
PUBLIC char *rpc_ptr;
PRIVATE struct channel rpc_chan;
/*===========================================================================*
* rpc_open *
*===========================================================================*/
PUBLIC int rpc_open()
{
/* Open a HGFS RPC backdoor channel to the VMware host, and make sure that it
* is working. Return OK upon success, or a negative error code otherwise.
*/
int r;
if ((r = channel_open(&rpc_chan, CH_OUT)) != OK)
return r;
r = rpc_test();
if (r != OK && r != EAGAIN)
channel_close(&rpc_chan);
return r;
}
/*===========================================================================*
* rpc_query *
*===========================================================================*/
PUBLIC int rpc_query()
{
/* Send a HGFS RPC query over the backdoor channel. Return OK upon success, or
* a negative error code otherwise; EAGAIN is returned if shared folders are
* disabled. In general, we make the assumption that the sender (= VMware)
* speaks the protocol correctly. Hence, the callers of this function do not
* check for lengths.
*/
int r, len, id, err;
len = RPC_LEN;
/* A more robust version of this call could reopen the channel and
* retry the request upon low-level failure.
*/
r = channel_send(&rpc_chan, rpc_buf, len);
if (r < 0) return r;
r = channel_recv(&rpc_chan, rpc_buf, sizeof(rpc_buf));
if (r < 0) return r;
if (r < 2 || (len > 2 && r < 10)) return EIO;
RPC_RESET;
if (RPC_NEXT8 != '1') return EAGAIN;
if (RPC_NEXT8 != ' ') return EAGAIN;
if (len <= 2) return OK;
id = RPC_NEXT32;
err = RPC_NEXT32;
return error_convert(err);
}
/*===========================================================================*
* rpc_test *
*===========================================================================*/
PUBLIC int rpc_test()
{
/* Test whether HGFS communication is working. Return OK on success, EAGAIN if
* shared folders are disabled, or another negative error code upon error.
*/
RPC_RESET;
RPC_NEXT8 = 'f';
RPC_NEXT8 = ' ';
return rpc_query();
}
/*===========================================================================*
* rpc_close *
*===========================================================================*/
PUBLIC void rpc_close()
{
/* Close the HGFS RPC backdoor channel.
*/
channel_close(&rpc_chan);
}

View file

@ -0,0 +1,69 @@
/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
#include "inc.h"
PRIVATE u64_t time_offset;
/*===========================================================================*
* time_init *
*===========================================================================*/
PUBLIC void time_init()
{
/* Initialize the time conversion module.
*/
/* Generate a 64-bit value for the offset to use in time conversion. The
* HGFS time format uses Windows' FILETIME standard, expressing time in
* 100ns-units since Jan 1, 1601 UTC. The value that is generated is
* 116444736000000000.
*/
/* FIXME: we currently do not take into account timezones. */
time_offset = make64(3577643008UL, 27111902UL);
}
/*===========================================================================*
* time_put *
*===========================================================================*/
PUBLIC void time_put(timep)
time_t *timep;
{
/* Store a UNIX timestamp pointed to by the given pointer onto the RPC buffer,
* in HGFS timestamp format. If a NULL pointer is given, store a timestamp of
* zero instead.
*/
u64_t hgfstime;
if (timep != NULL) {
hgfstime = add64(mul64u(*timep, 10000000), time_offset);
RPC_NEXT32 = ex64lo(hgfstime);
RPC_NEXT32 = ex64hi(hgfstime);
} else {
RPC_NEXT32 = 0;
RPC_NEXT32 = 0;
}
}
/*===========================================================================*
* time_get *
*===========================================================================*/
PUBLIC void time_get(timep)
time_t *timep;
{
/* Get a HGFS timestamp from the RPC buffer, convert it into a UNIX timestamp,
* and store the result in the given time pointer. If the given pointer is
* NULL, however, simply skip over the timestamp in the RPC buffer.
*/
u64_t hgfstime;
u32_t time_lo, time_hi;
if (timep != NULL) {
time_lo = RPC_NEXT32;
time_hi = RPC_NEXT32;
hgfstime = make64(time_lo, time_hi);
*timep = div64u(sub64(hgfstime, time_offset), 10000000);
}
else RPC_ADVANCE(sizeof(u32_t) * 2);
}

View file

@ -0,0 +1,7 @@
/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */
struct channel {
u16_t id;
u32_t cookie1;
u32_t cookie2;
};

403
servers/hgfs/link.c Normal file
View file

@ -0,0 +1,403 @@
/* This file contains directory entry related file system call handlers.
*
* The entry points into this file are:
* do_create perform the CREATE file system call
* do_mkdir perform the MKDIR file system call
* do_unlink perform the UNLINK file system call
* do_rmdir perform the RMDIR file system call
* do_rename perform the RENAME file system call
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
#include <fcntl.h>
FORWARD _PROTOTYPE( int force_remove, (char *path, int dir) );
/*===========================================================================*
* do_create *
*===========================================================================*/
PUBLIC int do_create()
{
/* Create a new file.
*/
char path[PATH_MAX], name[NAME_MAX+1];
struct inode *parent, *ino;
struct hgfs_attr attr;
hgfs_file_t handle;
int r;
/* We cannot create files on a read-only file system. */
if (state.read_only)
return EROFS;
/* Get path, name, parent inode and possibly inode for the given path. */
if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
return r;
if (!strcmp(name, ".") || !strcmp(name, "..")) return EEXIST;
if ((parent = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
return EINVAL;
if ((r = verify_dentry(parent, name, path, &ino)) != OK)
return r;
/* Are we going to need a new inode upon success?
* Then make sure there is one available before trying anything.
*/
if (ino == NIL_INODE || ino->i_ref > 1 || HAS_CHILDREN(ino)) {
if (!have_free_inode()) {
if (ino != NIL_INODE)
put_inode(ino);
return ENFILE;
}
}
/* Perform the actual create call. */
r = hgfs_open(path, O_CREAT | O_EXCL | O_RDWR, m_in.REQ_MODE, &handle);
if (r != OK) {
/* Let's not try to be too clever with error codes here. If something
* is wrong with the directory, we'll find out later anyway.
*/
if (ino != NIL_INODE)
put_inode(ino);
return r;
}
/* Get the created file's attributes. */
attr.a_mask = HGFS_ATTR_MODE | HGFS_ATTR_SIZE;
r = hgfs_getattr(path, &attr);
/* If this fails, or returns a directory, we have a problem. This
* scenario is in fact possible with race conditions.
* Simulate a close and return a somewhat appropriate error.
*/
if (r != OK || S_ISDIR(attr.a_mode)) {
printf("HGFS: lost file after creation!\n");
hgfs_close(handle);
if (ino != NIL_INODE) {
del_dentry(ino);
put_inode(ino);
}
return (r == OK) ? EEXIST : r;
}
/* We do assume that the HGFS open(O_CREAT|O_EXCL) did its job.
* If we previousy found an inode, get rid of it now. It's old.
*/
if (ino != NIL_INODE) {
del_dentry(ino);
put_inode(ino);
}
/* Associate the open file handle with an inode, and reply with its details.
*/
ino = get_free_inode();
assert(ino != NIL_INODE); /* we checked before whether we had a free one */
ino->i_file = handle;
ino->i_flags = I_HANDLE;
add_dentry(parent, name, ino);
m_out.RES_INODE_NR = INODE_NR(ino);
m_out.RES_MODE = get_mode(ino, attr.a_mode);
m_out.RES_FILE_SIZE_HI = ex64hi(attr.a_size);
m_out.RES_FILE_SIZE_LO = ex64lo(attr.a_size);
m_out.RES_UID = opt.uid;
m_out.RES_GID = opt.gid;
m_out.RES_DEV = NO_DEV;
return OK;
}
/*===========================================================================*
* do_mkdir *
*===========================================================================*/
PUBLIC int do_mkdir()
{
/* Make a new directory.
*/
char path[PATH_MAX], name[NAME_MAX+1];
struct inode *parent, *ino;
int r;
/* We cannot create directories on a read-only file system. */
if (state.read_only)
return EROFS;
/* Get the path string and possibly an inode for the given path. */
if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
return r;
if (!strcmp(name, ".") || !strcmp(name, "..")) return EEXIST;
if ((parent = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
return EINVAL;
if ((r = verify_dentry(parent, name, path, &ino)) != OK)
return r;
/* Perform the actual mkdir call. */
r = hgfs_mkdir(path, m_in.REQ_MODE);
if (r != OK) {
if (ino != NIL_INODE)
put_inode(ino);
return r;
}
/* If we thought the new dentry already existed, it was apparently gone
* already. Delete it.
*/
if (ino != NIL_INODE) {
del_dentry(ino);
put_inode(ino);
}
return OK;
}
/*===========================================================================*
* force_remove *
*===========================================================================*/
PRIVATE int force_remove(path, dir)
char *path; /* path to file or directory */
int dir; /* TRUE iff directory */
{
/* Remove a file or directory. Wrapper around hgfs_unlink and hgfs_rmdir that
* makes the target temporarily writable if the operation fails with an access
* denied error. On Windows hosts, read-only files or directories cannot be
* removed (even though they can be renamed). In general, the HGFS server
* follows the behavior of the host file system, but this case just confuses
* the hell out of the MINIX userland..
*/
struct hgfs_attr attr;
int r, r2;
/* First try to remove the target. */
if (dir)
r = hgfs_rmdir(path);
else
r = hgfs_unlink(path);
if (r != EACCES) return r;
/* If this fails with an access error, retrieve the target's mode. */
attr.a_mask = HGFS_ATTR_MODE;
r2 = hgfs_getattr(path, &attr);
if (r2 != OK || (attr.a_mode & S_IWUSR)) return r;
/* If the target is not writable, temporarily set it to writable. */
attr.a_mode |= S_IWUSR;
r2 = hgfs_setattr(path, &attr);
if (r2 != OK) return r;
/* Then try the original operation again. */
if (dir)
r = hgfs_rmdir(path);
else
r = hgfs_unlink(path);
if (r == OK) return r;
/* If the operation still fails, unset the writable bit again. */
attr.a_mode &= ~S_IWUSR;
hgfs_setattr(path, &attr);
return r;
}
/*===========================================================================*
* do_unlink *
*===========================================================================*/
PUBLIC int do_unlink()
{
/* Delete a file.
*/
char path[PATH_MAX], name[NAME_MAX+1];
struct inode *parent, *ino;
int r;
/* We cannot delete files on a read-only file system. */
if (state.read_only)
return EROFS;
/* Get the path string and possibly preexisting inode for the given path. */
if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
return r;
if (!strcmp(name, ".") || !strcmp(name, "..")) return EPERM;
if ((parent = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
return EINVAL;
if ((r = verify_dentry(parent, name, path, &ino)) != OK)
return r;
/* Perform the unlink call. */
r = force_remove(path, FALSE /*dir*/);
if (r != OK) {
if (ino != NIL_INODE)
put_inode(ino);
return r;
}
/* If a dentry existed for this name, it is gone now. */
if (ino != NIL_INODE) {
del_dentry(ino);
put_inode(ino);
}
return OK;
}
/*===========================================================================*
* do_rmdir *
*===========================================================================*/
PUBLIC int do_rmdir()
{
/* Remove an empty directory.
*/
char path[PATH_MAX], name[NAME_MAX+1];
struct inode *parent, *ino;
int r;
/* We cannot remove directories on a read-only file system. */
if (state.read_only)
return EROFS;
/* Get the path string and possibly preexisting inode for the given path. */
if ((r = get_name(m_in.REQ_GRANT, m_in.REQ_PATH_LEN, name)) != OK)
return r;
if (!strcmp(name, ".")) return EINVAL;
if (!strcmp(name, "..")) return ENOTEMPTY;
if ((parent = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
return EINVAL;
if ((r = verify_dentry(parent, name, path, &ino)) != OK)
return r;
/* Perform the rmdir call. */
r = force_remove(path, TRUE /*dir*/);
if (r != OK) {
if (ino != NIL_INODE)
put_inode(ino);
return r;
}
/* If a dentry existed for this name, it is gone now. */
if (ino != NIL_INODE) {
del_dentry(ino);
put_inode(ino);
}
return OK;
}
/*===========================================================================*
* do_rename *
*===========================================================================*/
PUBLIC int do_rename()
{
/* Rename a file or directory.
*/
char old_path[PATH_MAX], new_path[PATH_MAX];
char old_name[NAME_MAX+1], new_name[NAME_MAX+1];
struct inode *old_parent, *new_parent;
struct inode *old_ino, *new_ino;
int r;
/* We cannot do rename on a read-only file system. */
if (state.read_only)
return EROFS;
/* Get path strings, names, directory inodes and possibly preexisting inodes
* for the old and new paths.
*/
if ((r = get_name(m_in.REQ_REN_GRANT_OLD, m_in.REQ_REN_LEN_OLD,
old_name)) != OK) return r;
if ((r = get_name(m_in.REQ_REN_GRANT_NEW, m_in.REQ_REN_LEN_NEW,
new_name)) != OK) return r;
if (!strcmp(old_name, ".") || !strcmp(old_name, "..") ||
!strcmp(new_name, ".") || !strcmp(new_name, "..")) return EINVAL;
if ((old_parent = find_inode(m_in.REQ_REN_OLD_DIR)) == NIL_INODE ||
(new_parent = find_inode(m_in.REQ_REN_NEW_DIR)) == NIL_INODE)
return EINVAL;
if ((r = verify_dentry(old_parent, old_name, old_path, &old_ino)) != OK)
return r;
if ((r = verify_dentry(new_parent, new_name, new_path, &new_ino)) != OK) {
if (old_ino != NIL_INODE)
put_inode(old_ino);
return r;
}
/* Perform the actual rename call. */
r = hgfs_rename(old_path, new_path);
/* If we failed, or if we have nothing further to do: both inodes are
* NULL, or they both refer to the same file.
*/
if (r != OK || old_ino == new_ino) {
if (old_ino != NIL_INODE) put_inode(old_ino);
if (new_ino != NIL_INODE) put_inode(new_ino);
return r;
}
/* If the new dentry already existed, it has now been overwritten.
* Delete the associated inode if we had found one.
*/
if (new_ino != NIL_INODE) {
del_dentry(new_ino);
put_inode(new_ino);
}
/* If the old dentry existed, rename it accordingly. */
if (old_ino != NIL_INODE) {
del_dentry(old_ino);
add_dentry(new_parent, new_name, old_ino);
put_inode(old_ino);
}
return OK;
}

333
servers/hgfs/lookup.c Normal file
View file

@ -0,0 +1,333 @@
/* This file provides path-to-inode lookup functionality.
*
* The entry points into this file are:
* do_lookup perform the LOOKUP file system call
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
FORWARD _PROTOTYPE( int get_mask, (vfs_ucred_t *ucred) );
FORWARD _PROTOTYPE( int access_as_dir, (struct inode *ino,
struct hgfs_attr *attr, int uid, int mask) );
FORWARD _PROTOTYPE( int next_name, (char **ptr, char **start,
char name[NAME_MAX+1]) );
FORWARD _PROTOTYPE( int go_up, (char path[PATH_MAX], struct inode *ino,
struct inode **res_ino, struct hgfs_attr *attr) );
FORWARD _PROTOTYPE( int go_down, (char path[PATH_MAX],
struct inode *ino, char *name,
struct inode **res_ino, struct hgfs_attr *attr) );
/*===========================================================================*
* get_mask *
*===========================================================================*/
PRIVATE int get_mask(ucred)
vfs_ucred_t *ucred; /* credentials of the caller */
{
/* Given the caller's credentials, precompute a search access mask to test
* against directory modes.
*/
int i;
if (ucred->vu_uid == opt.uid) return S_IXUSR;
if (ucred->vu_gid == opt.gid) return S_IXGRP;
for (i = 0; i < ucred->vu_ngroups; i++)
if (ucred->vu_sgroups[i] == opt.gid) return S_IXGRP;
return S_IXOTH;
}
/*===========================================================================*
* access_as_dir *
*===========================================================================*/
PRIVATE int access_as_dir(ino, attr, uid, mask)
struct inode *ino; /* the inode to test */
struct hgfs_attr *attr; /* attributes of the inode */
int uid; /* UID of the caller */
int mask; /* search access mask of the caller */
{
/* Check whether the given inode may be accessed as directory.
* Return OK or an appropriate error code.
*/
mode_t mode;
assert(attr->a_mask & HGFS_ATTR_MODE);
/* The inode must be a directory to begin with. */
if (!IS_DIR(ino)) return ENOTDIR;
/* The caller must have search access to the directory. Root always does. */
if (uid == 0) return OK;
mode = get_mode(ino, attr->a_mode);
return (mode & mask) ? OK : EACCES;
}
/*===========================================================================*
* next_name *
*===========================================================================*/
PRIVATE int next_name(ptr, start, name)
char **ptr; /* cursor pointer into path (in, out) */
char **start; /* place to store start of name */
char name[NAME_MAX+1]; /* place to store name */
{
/* Get the next path component from a path.
*/
char *p;
int i;
for (p = *ptr; *p == '/'; p++);
*start = p;
if (*p) {
for (i = 0; *p && *p != '/' && i <= NAME_MAX; p++, i++)
name[i] = *p;
if (i > NAME_MAX)
return ENAMETOOLONG;
name[i] = 0;
} else {
strcpy(name, ".");
}
*ptr = p;
return OK;
}
/*===========================================================================*
* go_up *
*===========================================================================*/
PRIVATE int go_up(path, ino, res_ino, attr)
char path[PATH_MAX]; /* path to take the last part from */
struct inode *ino; /* inode of the current directory */
struct inode **res_ino; /* place to store resulting inode */
struct hgfs_attr *attr; /* place to store inode attributes */
{
/* Given an inode, progress into the parent directory.
*/
struct inode *parent;
int r;
pop_path(path);
parent = ino->i_parent;
assert(parent != NIL_INODE);
if ((r = verify_path(path, parent, attr, NULL)) != OK)
return r;
get_inode(parent);
*res_ino = parent;
return r;
}
/*===========================================================================*
* go_down *
*===========================================================================*/
PRIVATE int go_down(path, parent, name, res_ino, attr)
char path[PATH_MAX]; /* path to add the name to */
struct inode *parent; /* inode of the current directory */
char *name; /* name of the directory entry */
struct inode **res_ino; /* place to store resulting inode */
struct hgfs_attr *attr; /* place to store inode attributes */
{
/* Given a directory inode and a name, progress into a directory entry.
*/
struct inode *ino;
int r, stale = 0;
if ((r = push_path(path, name)) != OK)
return r;
dprintf(("HGFS: go_down: name '%s', path now '%s'\n", name, path));
ino = lookup_dentry(parent, name);
dprintf(("HGFS: lookup_dentry('%s') returned %p\n", name, ino));
if (ino != NIL_INODE)
r = verify_path(path, ino, attr, &stale);
else
r = hgfs_getattr(path, attr);
dprintf(("HGFS: path query returned %d\n", r));
if (r != OK) {
if (ino != NIL_INODE) {
put_inode(ino);
ino = NIL_INODE;
}
if (!stale)
return r;
}
dprintf(("HGFS: name '%s'\n", name));
if (ino == NIL_INODE) {
if ((ino = get_free_inode()) == NIL_INODE)
return ENFILE;
dprintf(("HGFS: inode %p ref %d\n", ino, ino->i_ref));
ino->i_flags = MODE_TO_DIRFLAG(attr->a_mode);
add_dentry(parent, name, ino);
}
*res_ino = ino;
return OK;
}
/*===========================================================================*
* do_lookup *
*===========================================================================*/
PUBLIC int do_lookup()
{
/* Resolve a path string to an inode.
*/
ino_t dir_ino_nr, root_ino_nr;
struct inode *cur_ino, *next_ino, *root_ino;
struct hgfs_attr attr;
char buf[PATH_MAX], path[PATH_MAX];
char name[NAME_MAX+1];
char *ptr, *last;
vfs_ucred_t ucred;
mode_t mask;
size_t len;
int r;
dir_ino_nr = m_in.REQ_DIR_INO;
root_ino_nr = m_in.REQ_ROOT_INO;
len = m_in.REQ_PATH_LEN;
/* Fetch the path name. */
if (len < 1 || len > PATH_MAX)
return EINVAL;
r = sys_safecopyfrom(m_in.m_source, m_in.REQ_GRANT, 0,
(vir_bytes) buf, len, D);
if (r != OK)
return r;
if (buf[len-1] != 0) {
printf("HGFS: VFS did not zero-terminate path!\n");
return EINVAL;
}
/* Fetch the credentials, and generate a search access mask to test against
* directory modes.
*/
if (m_in.REQ_FLAGS & PATH_GET_UCRED) {
if (m_in.REQ_UCRED_SIZE != sizeof(ucred)) {
printf("HGFS: bad credential structure size\n");
return EINVAL;
}
r = sys_safecopyfrom(m_in.m_source, m_in.REQ_GRANT2, 0,
(vir_bytes) &ucred, m_in.REQ_UCRED_SIZE, D);
if (r != OK)
return r;
}
else {
ucred.vu_uid = m_in.REQ_UID;
ucred.vu_gid = m_in.REQ_GID;
ucred.vu_ngroups = 0;
}
mask = get_mask(&ucred);
/* Start the actual lookup. */
dprintf(("HGFS: lookup: got query '%s'\n", buf));
if ((cur_ino = find_inode(dir_ino_nr)) == NIL_INODE)
return EINVAL;
attr.a_mask = HGFS_ATTR_MODE | HGFS_ATTR_SIZE;
if ((r = verify_inode(cur_ino, path, &attr)) != OK)
return r;
get_inode(cur_ino);
if (root_ino_nr > 0)
root_ino = find_inode(root_ino_nr);
else
root_ino = NIL_INODE;
/* One possible optimization would be to check a path only right before the
* first ".." in a row, and at the very end (if still necessary). This would
* have consequences for inode validation, though.
*/
for (ptr = last = buf; *ptr != 0; ) {
if ((r = access_as_dir(cur_ino, &attr, ucred.vu_uid, mask)) != OK)
break;
if ((r = next_name(&ptr, &last, name)) != OK)
break;
dprintf(("HGFS: lookup: next name '%s'\n", name));
if (!strcmp(name, ".") ||
(cur_ino == root_ino && !strcmp(name, "..")))
continue;
if (!strcmp(name, "..")) {
if (IS_ROOT(cur_ino))
r = ELEAVEMOUNT;
else
r = go_up(path, cur_ino, &next_ino, &attr);
} else {
r = go_down(path, cur_ino, name, &next_ino, &attr);
}
if (r != OK)
break;
assert(next_ino != NIL_INODE);
put_inode(cur_ino);
cur_ino = next_ino;
}
dprintf(("HGFS: lookup: result %d\n", r));
if (r != OK) {
put_inode(cur_ino);
/* We'd need support for these here. We don't have such support. */
assert(r != EENTERMOUNT && r != ESYMLINK);
if (r == ELEAVEMOUNT) {
m_out.RES_OFFSET = (int)(last - buf);
m_out.RES_SYMLOOP = 0;
}
return r;
}
m_out.RES_INODE_NR = INODE_NR(cur_ino);
m_out.RES_MODE = get_mode(cur_ino, attr.a_mode);
m_out.RES_FILE_SIZE_HI = ex64hi(attr.a_size);
m_out.RES_FILE_SIZE_LO = ex64lo(attr.a_size);
m_out.RES_UID = opt.uid;
m_out.RES_GID = opt.gid;
m_out.RES_DEV = NO_DEV;
return OK;
}

232
servers/hgfs/main.c Normal file
View file

@ -0,0 +1,232 @@
/* This file contains the main message loop of the HGFS file system server.
*
* The entry points into this file are:
* main main program function
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
#include "optset.h"
#include <signal.h>
#include <unistd.h>
FORWARD _PROTOTYPE( int init, (int type, sef_init_info_t *info) );
FORWARD _PROTOTYPE( void sef_local_startup, (void) );
FORWARD _PROTOTYPE( void cleanup, (void) );
FORWARD _PROTOTYPE( int get_work, (endpoint_t *who_e) );
FORWARD _PROTOTYPE( void send_reply, (int err) );
FORWARD _PROTOTYPE( int proc_event, (void) );
PRIVATE struct optset optset_table[] = {
{ "prefix", OPT_STRING, opt.prefix, sizeof(opt.prefix) },
{ "uid", OPT_INT, &opt.uid, 10 },
{ "gid", OPT_INT, &opt.gid, 10 },
{ "fmask", OPT_INT, &opt.file_mask, 8 },
{ "dmask", OPT_INT, &opt.dir_mask, 8 },
{ "icase", OPT_BOOL, &opt.case_insens, TRUE },
{ "noicase", OPT_BOOL, &opt.case_insens, FALSE },
{ NULL }
};
EXTERN int env_argc;
EXTERN char **env_argv;
/*===========================================================================*
* init *
*===========================================================================*/
PRIVATE int init(type, info)
int type;
sef_init_info_t *info;
{
/* Initialize this file server. Called at startup time.
*/
message m;
int i, r;
/* Defaults */
opt.prefix[0] = 0;
opt.uid = 0;
opt.gid = 0;
opt.file_mask = 0755;
opt.dir_mask = 0755;
opt.case_insens = FALSE;
/* If we have been given an options string, parse options from there. */
for (i = 1; i < env_argc - 1; i++)
if (!strcmp(env_argv[i], "-o"))
optset_parse(optset_table, env_argv[++i]);
/* Make sure that the given path prefix doesn't end with a slash. */
for (i = strlen(opt.prefix); i > 0 && opt.prefix[i - 1] == '/'; i--);
opt.prefix[i] = 0;
/* Initialize the HGFS library. If this fails, exit immediately. */
r = hgfs_init();
if (r != OK && r != EAGAIN) {
printf("HGFS: unable to initialize HGFS library (%d)\n", r);
return r;
}
state.mounted = FALSE;
/* Announce our presence to VFS. */
m.m_type = FS_READY;
if ((r = send(FS_PROC_NR, &m)) != OK) {
printf("HGFS: unable to login to VFS (%d)\n", r);
return r;
}
return OK;
}
/*===========================================================================*
* sef_local_startup *
*===========================================================================*/
PRIVATE void sef_local_startup()
{
/* Specify initialization routines and start the SEF framework.
*/
sef_setcb_init_fresh(init);
sef_setcb_init_restart(init);
/* No live update support yet. */
sef_startup();
}
/*===========================================================================*
* cleanup *
*===========================================================================*/
PRIVATE void cleanup()
{
/* Clean up any resources in use by this file server. Called at shutdown time.
*/
/* Pass on the cleanup request to the HGFS library. */
hgfs_cleanup();
}
/*===========================================================================*
* main *
*===========================================================================*/
PUBLIC int main(argc, argv)
int argc;
char *argv[];
{
/* The main function of this file server. After initializing, loop forever
* receiving one request from VFS at a time, processing it, and sending a
* reply back to VFS.
*/
endpoint_t who_e;
int call_nr, err;
env_setargs(argc, argv);
sef_local_startup();
for (;;) {
call_nr = get_work(&who_e);
if (who_e != FS_PROC_NR) {
/* Is this PM telling us to shut down? */
if (who_e == PM_PROC_NR && is_notify(call_nr))
if (proc_event()) break;
continue;
}
if (state.mounted || call_nr == REQ_READSUPER) {
call_nr -= VFS_BASE;
dprintf(("HGFS: call %d\n", call_nr));
if (call_nr >= 0 && call_nr < NREQS) {
err = (*call_vec[call_nr])();
} else {
err = ENOSYS;
}
dprintf(("HGFS: call %d result %d\n", call_nr, err));
}
else err = EINVAL;
send_reply(err);
}
cleanup();
return 0;
}
/*===========================================================================*
* get_work *
*===========================================================================*/
PRIVATE int get_work(who_e)
endpoint_t *who_e;
{
/* Receive a request message from VFS. Return the request call number.
*/
int r;
if ((r = sef_receive(ANY, &m_in)) != OK)
panic("HGFS", "receive failed", r);
*who_e = m_in.m_source;
return m_in.m_type;
}
/*===========================================================================*
* send_reply *
*===========================================================================*/
PRIVATE void send_reply(err)
int err; /* resulting error code */
{
/* Send a reply message to the requesting party, with the given error code.
*/
int r;
m_out.m_type = err;
if ((r = send(m_in.m_source, &m_out)) != OK)
printf("HGFS: send failed (%d)\n", r);
}
/*===========================================================================*
* proc_event *
*===========================================================================*/
PRIVATE int proc_event()
{
/* We got a notification from PM; see what it's about. Return TRUE if this
* server has been told to shut down.
*/
sigset_t set;
int r;
if ((r = getsigset(&set)) != OK) {
printf("HGFS: unable to get pending signals from PM (%d)\n", r);
return FALSE;
}
if (sigismember(&set, SIGTERM)) {
if (state.mounted) {
dprintf(("HGFS: got SIGTERM, still mounted\n"));
return FALSE;
}
dprintf(("HGFS: got SIGTERM, shutting down\n"));
return TRUE;
}
return FALSE;
}

27
servers/hgfs/misc.c Normal file
View file

@ -0,0 +1,27 @@
/* This file contains miscellaneous file system call handlers.
*
* The entry points into this file are:
* do_fstatfs perform the FSTATFS file system call
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
#include <sys/statfs.h>
/*===========================================================================*
* do_fstatfs *
*===========================================================================*/
PUBLIC int do_fstatfs()
{
/* Retrieve file system statistics.
*/
struct statfs statfs;
statfs.f_bsize = BLOCK_SIZE; /* arbitrary block size constant */
return sys_safecopyto(m_in.m_source, m_in.REQ_GRANT, 0,
(vir_bytes) &statfs, sizeof(statfs), D);
}

94
servers/hgfs/mount.c Normal file
View file

@ -0,0 +1,94 @@
/* This file contains mount and unmount functionality.
*
* The entry points into this file are:
* do_readsuper perform the READSUPER file system call
* do_unmount perform the UNMOUNT file system call
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
/*===========================================================================*
* do_readsuper *
*===========================================================================*/
PUBLIC int do_readsuper()
{
/* Mount the file system.
*/
char path[PATH_MAX];
struct inode *ino;
struct hgfs_attr attr;
int r;
dprintf(("HGFS: readsuper (dev %x, flags %x)\n",
m_in.REQ_DEV, m_in.REQ_FLAGS));
if (m_in.REQ_FLAGS & REQ_ISROOT) {
printf("HGFS: attempt to mount as root device\n");
return EINVAL;
}
state.read_only = !!(m_in.REQ_FLAGS & REQ_RDONLY);
state.dev = m_in.REQ_DEV;
init_dentry();
ino = init_inode();
attr.a_mask = HGFS_ATTR_MODE | HGFS_ATTR_SIZE;
/* We cannot continue if we fail to get the properties of the root inode at
* all, because we cannot guess the details of the root node to return to
* VFS. Print a (hopefully) helpful error message, and abort the mount.
*/
if ((r = verify_inode(ino, path, &attr)) != OK) {
if (r == EAGAIN)
printf("HGFS: shared folders disabled\n");
else if (opt.prefix[0] && (r == ENOENT || r == EACCES))
printf("HGFS: unable to access the given prefix directory\n");
else
printf("HGFS: unable to access shared folders\n");
return r;
}
m_out.RES_INODE_NR = INODE_NR(ino);
m_out.RES_MODE = get_mode(ino, attr.a_mode);
m_out.RES_FILE_SIZE_HI = ex64hi(attr.a_size);
m_out.RES_FILE_SIZE_LO = ex64lo(attr.a_size);
m_out.RES_UID = opt.uid;
m_out.RES_GID = opt.gid;
m_out.RES_DEV = NO_DEV;
state.mounted = TRUE;
return OK;
}
/*===========================================================================*
* do_unmount *
*===========================================================================*/
PUBLIC int do_unmount()
{
/* Unmount the file system.
*/
struct inode *ino;
dprintf(("HGFS: do_unmount\n"));
/* Decrease the reference count of the root inode. */
if ((ino = find_inode(ROOT_INODE_NR)) == NIL_INODE)
return EINVAL;
put_inode(ino);
/* There should not be any referenced inodes anymore now. */
if (have_used_inode())
printf("HGFS: in-use inodes left at unmount time!\n");
state.mounted = FALSE;
return OK;
}

56
servers/hgfs/name.c Normal file
View file

@ -0,0 +1,56 @@
/* This file contains path component name utility functions.
*
* The entry points into this file are:
* normalize_name normalize a path component name for hashing purposes
* compare_name check whether two path component names are equivalent
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
#include <ctype.h>
/*===========================================================================*
* normalize_name *
*===========================================================================*/
PUBLIC void normalize_name(dst, src)
char dst[NAME_MAX+1];
char *src;
{
/* Normalize the given path component name, storing the result in the given
* buffer.
*/
size_t i, size;
size = strlen(src) + 1;
assert(size <= NAME_MAX+1);
if (opt.case_insens) {
for (i = 0; i < size; i++)
*dst++ = tolower(*src++);
}
else memcpy(dst, src, size);
}
/*===========================================================================*
* compare_name *
*===========================================================================*/
PUBLIC int compare_name(name1, name2)
char *name1;
char *name2;
{
/* Return TRUE if the given path component names are equivalent, FALSE
* otherwise.
*/
int r;
if (opt.case_insens)
r = strcasecmp(name1, name2);
else
r = strcmp(name1, name2);
return r ? FALSE : TRUE;
}

128
servers/hgfs/optset.c Normal file
View file

@ -0,0 +1,128 @@
/* This file provides functionality to parse strings of comma-separated
* options, each being either a single key name or a key=value pair, where the
* value may be enclosed in quotes. A table of optset entries is provided to
* determine which options are recognized, how to parse their values, and where
* to store those. Unrecognized options are silently ignored; improperly
* formatted options are silently set to reasonably acceptable values.
*
* The entry points into this file are:
* optset_parse parse the given options string using the given table
*
* Created:
* May 2009 (D.C. van Moolenbroek)
*/
#define _MINIX 1
#include <stdlib.h>
#include <string.h>
#include <minix/config.h>
#include <minix/const.h>
#include "optset.h"
FORWARD _PROTOTYPE( void optset_parse_entry, (struct optset *entry,
char *ptr, int len) );
/*===========================================================================*
* optset_parse_entry *
*===========================================================================*/
PRIVATE void optset_parse_entry(entry, ptr, len)
struct optset *entry;
char *ptr;
int len;
{
/* Parse and store the value of a single option.
*/
char *dst;
int val;
switch (entry->os_type) {
case OPT_BOOL:
*((int *) entry->os_ptr) = entry->os_val;
break;
case OPT_STRING:
if (len >= entry->os_val)
len = entry->os_val - 1;
dst = (char *) entry->os_ptr;
if (len > 0)
memcpy(dst, ptr, len);
dst[len] = 0;
break;
case OPT_INT:
if (len > 0)
val = strtol(ptr, NULL, entry->os_val);
else
val = 0;
*((int *) entry->os_ptr) = val;
break;
}
}
/*===========================================================================*
* optset_parse *
*===========================================================================*/
PUBLIC void optset_parse(table, string)
struct optset *table;
char *string;
{
/* Parse a string of options, using the provided table of optset entries.
*/
char *p, *kptr, *vptr;
int i, klen, vlen;
for (p = string; *p; ) {
/* Get the key name for the field. */
for (kptr = p, klen = 0; *p && *p != '=' && *p != ','; p++, klen++);
if (*p == '=') {
/* The field has an associated value. */
vptr = ++p;
/* If the first character after the '=' is a quote character,
* find a matching quote character followed by either a comma
* or the terminating null character, and use the string in
* between. Otherwise, use the string up to the next comma or
* the terminating null character.
*/
if (*p == '\'' || *p == '"') {
p++;
for (vlen = 0; *p && (*p != *vptr ||
(p[1] && p[1] != ',')); p++, vlen++);
if (*p) p++;
vptr++;
}
else
for (vlen = 0; *p && *p != ','; p++, vlen++);
}
else {
vptr = NULL;
vlen = 0;
}
if (*p == ',') p++;
/* Find a matching entry for this key in the given table. If found,
* call optset_parse_entry() on it. Silently ignore the option
* otherwise.
*/
for (i = 0; table[i].os_name != NULL; i++) {
if (strlen(table[i].os_name) == klen &&
!strncasecmp(table[i].os_name, kptr, klen)) {
optset_parse_entry(&table[i], vptr, vlen);
break;
}
}
}
}

30
servers/hgfs/optset.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef _OPTSET_H
#define _OPTSET_H
enum {
OPT_BOOL,
OPT_STRING,
OPT_INT
};
/* An entry for the parser of an options set. The 'os_name' field must point
* to a string, which is treated case-insensitively; the last entry of a table
* must have NULL name. The 'os_type' field must be set to one of the OPT_
* values defined above. The 'os_ptr' field must point to the field that is to
* receive the value of a recognized option. For OPT_STRING, it must point to a
* string of a size set in 'os_val'; the resulting string may be truncated, but
* will always be null-terminated. For OPT_BOOL, it must point to an int which
* will be set to the value in 'os_val' if the option is present. For OPT_INT,
* it must point to an int which will be set to the provided option value;
* 'os_val' is then a base passed to strtol().
*/
struct optset {
char *os_name;
int os_type;
void *os_ptr;
int os_val;
};
_PROTOTYPE( void optset_parse, (struct optset *table, char *string) );
#endif /* _OPTSET_H */

112
servers/hgfs/path.c Normal file
View file

@ -0,0 +1,112 @@
/* This file contains routines for creating and manipulating path strings.
*
* The entry points into this file are:
* make_path construct a path string for an inode
* push_path add a path component to the end of a path string
* pop_path remove the last path component from a path string
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
/*===========================================================================*
* make_path *
*===========================================================================*/
PUBLIC int make_path(path, ino)
char path[PATH_MAX];
struct inode *ino;
{
/* Given an inode, construct the path identifying that inode.
*/
char buf[PATH_MAX], *p, *prefix;
size_t len, plen, total;
p = &buf[sizeof(buf) - 1];
p[0] = 0;
dprintf(("HGFS: make_path: constructing path for inode %d\n", ino->i_num));
/* Get the length of the prefix, skipping any leading slashes. */
for (prefix = opt.prefix; prefix[0] == '/'; prefix++);
plen = strlen(prefix);
/* Construct the path right-to-left in a temporary buffer first. */
for (total = plen; ino != NIL_INODE && !IS_ROOT(ino); ino = ino->i_parent) {
len = strlen(ino->i_name);
total += len + 1;
p -= len + 1;
if (total >= sizeof(buf))
return ENAMETOOLONG;
p[0] = '/';
memcpy(p + 1, ino->i_name, len);
}
/* If any of the intermediate inodes has no parent, the final inode is no
* longer addressable by name.
*/
if (ino == NIL_INODE)
return ENOENT;
/* Put the result in the actual buffer. We need the leading slash in the
* temporary buffer only when the prefix is not empty.
*/
if (!prefix[0] && p[0] == '/') p++;
strcpy(path, prefix);
strcpy(&path[plen], p);
dprintf(("HGFS: make_path: resulting path is '%s'\n", path));
return OK;
}
/*===========================================================================*
* push_path *
*===========================================================================*/
PUBLIC int push_path(path, name)
char path[PATH_MAX];
char *name;
{
/* Add a component to the end of a path.
*/
size_t len, add;
len = strlen(path);
add = strlen(name);
if (len > 0) add++;
if (len + add >= PATH_MAX)
return ENAMETOOLONG;
if (len > 0) path[len++] = '/';
strcpy(&path[len], name);
return OK;
}
/*===========================================================================*
* pop_path *
*===========================================================================*/
PUBLIC void pop_path(path)
char path[PATH_MAX];
{
/* Remove the last component from a path.
*/
char *p;
p = strrchr(path, '/');
if (p == NULL) {
p = path;
/* Can't pop the root component */
assert(p[0] != 0);
}
p[0] = 0;
}

82
servers/hgfs/proto.h Normal file
View file

@ -0,0 +1,82 @@
/* dentry.c */
_PROTOTYPE( void init_dentry, (void) );
_PROTOTYPE( struct inode *lookup_dentry, (struct inode *parent,
char *name) );
_PROTOTYPE( void add_dentry, (struct inode *parent, char *name,
struct inode *ino) );
_PROTOTYPE( void del_dentry, (struct inode *ino) );
/* handle.c */
_PROTOTYPE( int get_handle, (struct inode *ino) );
_PROTOTYPE( void put_handle, (struct inode *ino) );
/* inode.c */
_PROTOTYPE( struct inode *init_inode, (void) );
_PROTOTYPE( struct inode *find_inode, (ino_t ino_nr) );
_PROTOTYPE( void get_inode, (struct inode *ino) );
_PROTOTYPE( void put_inode, (struct inode *ino) );
_PROTOTYPE( void link_inode, (struct inode *parent, struct inode *ino) );
_PROTOTYPE( void unlink_inode, (struct inode *ino) );
_PROTOTYPE( struct inode *get_free_inode, (void) );
_PROTOTYPE( int have_free_inode, (void) );
_PROTOTYPE( int have_used_inode, (void) );
_PROTOTYPE( int do_putnode, (void) );
/* link.c */
_PROTOTYPE( int do_create, (void) );
_PROTOTYPE( int do_mkdir, (void) );
_PROTOTYPE( int do_unlink, (void) );
_PROTOTYPE( int do_rmdir, (void) );
_PROTOTYPE( int do_rename, (void) );
/* lookup.c */
_PROTOTYPE( int do_lookup, (void) );
/* main.c */
_PROTOTYPE( int main, (int argc, char *argv[]) );
/* misc.c */
_PROTOTYPE( int do_fstatfs, (void) );
/* mount.c */
_PROTOTYPE( int do_readsuper, (void) );
_PROTOTYPE( int do_unmount, (void) );
/* name.c */
_PROTOTYPE( void normalize_name, (char dst[NAME_MAX+1], char *src) );
_PROTOTYPE( int compare_name, (char *name1, char *name2) );
/* path.c */
_PROTOTYPE( int make_path, (char path[PATH_MAX], struct inode *ino) );
_PROTOTYPE( int push_path, (char path[PATH_MAX], char *name) );
_PROTOTYPE( void pop_path, (char path[PATH_MAX]) );
/* read.c */
_PROTOTYPE( int do_read, (void) );
_PROTOTYPE( int do_getdents, (void) );
/* stat.c */
_PROTOTYPE( mode_t get_mode, (struct inode *ino, int mode) );
_PROTOTYPE( int do_stat, (void) );
_PROTOTYPE( int do_chmod, (void) );
_PROTOTYPE( int do_utime, (void) );
/* util.c */
_PROTOTYPE( int get_name, (cp_grant_id_t grant, size_t len,
char name[NAME_MAX+1]) );
_PROTOTYPE( int do_noop, (void) );
_PROTOTYPE( int no_sys, (void) );
/* verify.c */
_PROTOTYPE( int verify_path, (char *path, struct inode *ino,
struct hgfs_attr *attr, int *stale) );
_PROTOTYPE( int verify_inode, (struct inode *ino, char path[PATH_MAX],
struct hgfs_attr *attr) );
_PROTOTYPE( int verify_dentry, (struct inode *parent,
char name[NAME_MAX+1], char path[PATH_MAX],
struct inode **res_ino) );
/* write.c */
_PROTOTYPE( int do_write, (void) );
_PROTOTYPE( int do_ftrunc, (void) );

239
servers/hgfs/read.c Normal file
View file

@ -0,0 +1,239 @@
/* This file contains file and directory reading file system call handlers.
*
* The entry points into this file are:
* do_read perform the READ file system call
* do_getdents perform the GETDENTS file system call
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
#include <dirent.h>
#define DWORD_ALIGN(len) (((len) + sizeof(long) - 1) & ~(sizeof(long) - 1))
/*===========================================================================*
* do_read *
*===========================================================================*/
PUBLIC int do_read()
{
/* Read data from a file.
*/
struct inode *ino;
u64_t pos;
size_t count, size;
vir_bytes off;
char *ptr;
int r, chunk;
if ((ino = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
return EINVAL;
if (IS_DIR(ino)) return EISDIR;
if ((r = get_handle(ino)) != OK)
return r;
pos = make64(m_in.REQ_SEEK_POS_LO, m_in.REQ_SEEK_POS_HI);
count = m_in.REQ_NBYTES;
assert(count > 0);
/* Use the buffer from libhgfs to eliminate extra copying. */
size = hgfs_readbuf(&ptr);
off = 0;
while (count > 0) {
chunk = MIN(count, size);
if ((r = hgfs_read(ino->i_file, NULL, chunk, pos)) <= 0)
break;
chunk = r;
r = sys_safecopyto(m_in.m_source, m_in.REQ_GRANT, off,
(vir_bytes) ptr, chunk, D);
if (r != OK)
break;
count -= chunk;
off += chunk;
pos = add64u(pos, chunk);
}
if (r < 0)
return r;
m_out.RES_SEEK_POS_HI = ex64hi(pos);
m_out.RES_SEEK_POS_LO = ex64lo(pos);
m_out.RES_NBYTES = off;
return OK;
}
/*===========================================================================*
* do_getdents *
*===========================================================================*/
PUBLIC int do_getdents()
{
/* Retrieve directory entries.
*/
char name[NAME_MAX+1];
struct inode *ino, *child;
struct dirent *dent;
struct hgfs_attr attr;
size_t len, off, user_off, user_left;
off_t pos;
int r;
/* must be at least sizeof(struct dirent) + NAME_MAX */
static char buf[BLOCK_SIZE];
attr.a_mask = HGFS_ATTR_MODE;
if ((ino = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
return EINVAL;
if (m_in.REQ_SEEK_POS_HI != 0) return EINVAL;
if (!IS_DIR(ino)) return ENOTDIR;
/* We are going to need at least one free inode to store children in. */
if (!have_free_inode()) return ENFILE;
/* If we don't have a directory handle yet, get one now. */
if ((r = get_handle(ino)) != OK)
return r;
off = 0;
user_off = 0;
user_left = m_in.REQ_MEM_SIZE;
/* We use the seek position as file index number. The first position is for
* the "." entry, the second position is for the ".." entry, and the next
* position numbers each represent a file in the directory.
*/
for (pos = m_in.REQ_SEEK_POS_LO; ; pos++) {
/* Determine which inode and name to use for this entry.
* We have no idea whether the HGFS host will give us "." and/or "..",
* so generate our own and skip those from the host.
*/
if (pos == 0) {
/* Entry for ".". */
child = ino;
strcpy(name, ".");
get_inode(child);
}
else if (pos == 1) {
/* Entry for "..", but only when there is a parent. */
if (ino->i_parent == NIL_INODE)
continue;
child = ino->i_parent;
strcpy(name, "..");
get_inode(child);
}
else {
/* Any other entry, not being "." or "..". */
r = hgfs_readdir(ino->i_dir, pos - 2, name, sizeof(name),
&attr);
if (r != OK || !name[0]) {
/* No more entries? Then close the handle and stop. */
/* VMware Player 3 returns an empty name, instead of
* EINVAL, when reading from an EOF position right
* after opening the directory handle. Seems to be a
* newly introduced bug..
*/
if (r == EINVAL || !name[0]) {
put_handle(ino);
break;
}
/* FIXME: what if the error is ENAMETOOLONG? */
return r;
}
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
if ((child = lookup_dentry(ino, name)) == NIL_INODE) {
child = get_free_inode();
/* We were promised a free inode! */
assert(child != NIL_INODE);
child->i_flags = MODE_TO_DIRFLAG(attr.a_mode);
add_dentry(ino, name, child);
}
}
len = DWORD_ALIGN(sizeof(struct dirent) + strlen(name));
/* Is the user buffer too small to store another record?
* Note that we will be rerequesting the same dentry upon a subsequent
* getdents call this way, but we really need the name length for this.
*/
if (user_off + off + len > user_left) {
put_inode(child);
/* Is the user buffer too small for even a single record? */
if (user_off == 0 && off == 0)
return EINVAL;
break;
}
/* If our own buffer cannot contain the new record, copy out first. */
if (off + len > sizeof(buf)) {
r = sys_safecopyto(m_in.m_source, m_in.REQ_GRANT,
user_off, (vir_bytes) buf, off, D);
if (r != OK) {
put_inode(child);
return r;
}
user_off += off;
user_left -= off;
off = 0;
}
/* Fill in the actual directory entry. */
dent = (struct dirent *) &buf[off];
dent->d_ino = INODE_NR(child);
dent->d_off = pos;
dent->d_reclen = len;
strcpy(dent->d_name, name);
off += len;
put_inode(child);
}
/* If there is anything left in our own buffer, copy that out now. */
if (off > 0) {
r = sys_safecopyto(m_in.m_source, m_in.REQ_GRANT, user_off,
(vir_bytes) buf, off, D);
if (r != OK)
return r;
user_off += off;
}
m_out.RES_SEEK_POS_HI = 0;
m_out.RES_SEEK_POS_LO = pos;
m_out.RES_NBYTES = user_off;
return OK;
}

154
servers/hgfs/stat.c Normal file
View file

@ -0,0 +1,154 @@
/* This file contains file metadata retrieval and manipulation routines.
*
* The entry points into this file are:
* get_mode return a file's mode
* do_stat perform the STAT file system call
* do_chmod perform the CHMOD file system call
* do_utime perform the UTIME file system call
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
/*===========================================================================*
* get_mode *
*===========================================================================*/
PUBLIC mode_t get_mode(ino, mode)
struct inode *ino;
int mode;
{
/* Return the mode for an inode, given the inode and the HGFS retrieved mode.
*/
mode &= S_IRWXU;
mode = mode | (mode >> 3) | (mode >> 6);
if (IS_DIR(ino))
mode = S_IFDIR | (mode & opt.dir_mask);
else
mode = S_IFREG | (mode & opt.file_mask);
if (state.read_only)
mode &= ~0222;
return mode;
}
/*===========================================================================*
* do_stat *
*===========================================================================*/
PUBLIC int do_stat()
{
/* Retrieve inode statistics.
*/
struct inode *ino;
struct hgfs_attr attr;
struct stat stat;
char path[PATH_MAX];
ino_t ino_nr;
int r;
ino_nr = m_in.REQ_INODE_NR;
/* Don't increase the inode refcount: it's already open anyway */
if ((ino = find_inode(ino_nr)) == NIL_INODE)
return EINVAL;
attr.a_mask = HGFS_ATTR_MODE | HGFS_ATTR_SIZE | HGFS_ATTR_ATIME |
HGFS_ATTR_MTIME | HGFS_ATTR_CTIME;
if ((r = verify_inode(ino, path, &attr)) != OK)
return r;
stat.st_dev = state.dev;
stat.st_ino = ino_nr;
stat.st_mode = get_mode(ino, attr.a_mode);
stat.st_uid = opt.uid;
stat.st_gid = opt.gid;
stat.st_rdev = NO_DEV;
stat.st_size = ex64hi(attr.a_size) ? ULONG_MAX : ex64lo(attr.a_size);
stat.st_atime = attr.a_atime;
stat.st_mtime = attr.a_mtime;
stat.st_ctime = attr.a_ctime;
/* We could make this more accurate by iterating over directory inodes'
* children, counting how many of those are directories as well.
* It's just not worth it.
*/
stat.st_nlink = 0;
if (ino->i_parent != NIL_INODE) stat.st_nlink++;
if (IS_DIR(ino)) {
stat.st_nlink++;
if (HAS_CHILDREN(ino)) stat.st_nlink++;
}
return sys_safecopyto(m_in.m_source, m_in.REQ_GRANT, 0,
(vir_bytes) &stat, sizeof(stat), D);
}
/*===========================================================================*
* do_chmod *
*===========================================================================*/
PUBLIC int do_chmod()
{
/* Change file mode.
*/
struct inode *ino;
char path[PATH_MAX];
struct hgfs_attr attr;
int r;
if (state.read_only)
return EROFS;
if ((ino = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
return EINVAL;
if ((r = verify_inode(ino, path, NULL)) != OK)
return r;
/* Set the new file mode. */
attr.a_mask = HGFS_ATTR_MODE;
attr.a_mode = m_in.REQ_MODE; /* no need to convert in this direction */
if ((r = hgfs_setattr(path, &attr)) != OK)
return r;
/* We have no idea what really happened. Query for the mode again. */
if ((r = verify_path(path, ino, &attr, NULL)) != OK)
return r;
m_out.RES_MODE = get_mode(ino, attr.a_mode);
return OK;
}
/*===========================================================================*
* do_utime *
*===========================================================================*/
PUBLIC int do_utime()
{
/* Set file times.
*/
struct inode *ino;
char path[PATH_MAX];
struct hgfs_attr attr;
int r;
if (state.read_only)
return EROFS;
if ((ino = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
return EINVAL;
if ((r = verify_inode(ino, path, NULL)) != OK)
return r;
attr.a_mask = HGFS_ATTR_ATIME | HGFS_ATTR_MTIME;
attr.a_atime = m_in.REQ_ACTIME;
attr.a_mtime = m_in.REQ_MODTIME;
return hgfs_setattr(path, &attr);
}

46
servers/hgfs/table.c Normal file
View file

@ -0,0 +1,46 @@
/* This file contains the file system call table.
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#define _TABLE
#include "inc.h"
PUBLIC _PROTOTYPE( int (*call_vec[]), (void) ) = {
no_sys, /* 0 */
no_sys, /* 1 getnode */
do_putnode, /* 2 putnode */
no_sys, /* 3 slink */
do_ftrunc, /* 4 ftrunc */
no_sys, /* 5 chown */
do_chmod, /* 6 chmod */
do_noop, /* 7 inhibread */
do_stat, /* 8 stat */
do_utime, /* 9 utime */
do_fstatfs, /* 10 fstatfs */
no_sys, /* 11 bread */
no_sys, /* 12 bwrite */
do_unlink, /* 13 unlink */
do_rmdir, /* 14 rmdir */
do_unmount, /* 15 unmount */
do_noop, /* 16 sync */
do_noop, /* 17 new_driver */
do_noop, /* 18 flush */
do_read, /* 19 read */
do_write, /* 20 write */
no_sys, /* 21 mknod */
do_mkdir, /* 22 mkdir */
do_create, /* 23 create */
no_sys, /* 24 link */
do_rename, /* 25 rename */
do_lookup, /* 26 lookup */
no_sys, /* 27 mountpoint */
do_readsuper, /* 28 readsuper */
no_sys, /* 29 newnode */
no_sys, /* 30 rdlink */
do_getdents, /* 31 getdents */
};
/* This should not fail with "array size is negative": */
extern int dummy[sizeof(call_vec) == NREQS * sizeof(call_vec[0]) ? 1 : -1];

19
servers/hgfs/type.h Normal file
View file

@ -0,0 +1,19 @@
/* Structure with global file system state. */
struct state {
int mounted; /* is the file system mounted? */
int read_only; /* is the file system mounted read-only? note,
* has no relation to the shared folder mode */
dev_t dev; /* device the file system is mounted on */
};
/* Structure with options affecting global behavior. */
struct opt {
char prefix[PATH_MAX]; /* prefix for all paths used */
uid_t uid; /* UID that owns all files */
gid_t gid; /* GID that owns all files */
unsigned int file_mask; /* AND-mask to apply to file permissions */
unsigned int dir_mask; /* AND-mask to apply to directory perm's */
int case_insens; /* case insensitivity flag; has no relation
* to the hosts's shared folder naming */
};

63
servers/hgfs/util.c Normal file
View file

@ -0,0 +1,63 @@
/* This file contains various utility functions.
*
* The entry points into this file are:
* get_name retrieve a path component string from VFS
* do_noop handle file system calls that do nothing and succeed
* no_sys handle file system calls that are not implemented
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
/*===========================================================================*
* get_name *
*===========================================================================*/
PUBLIC int get_name(grant, len, name)
cp_grant_id_t grant; /* memory grant for the path component */
size_t len; /* length of the name, including '\0' */
char name[NAME_MAX+1]; /* buffer in which store the result */
{
/* Retrieve a path component from the caller, using a given grant.
*/
int r;
/* Copy in the name of the directory entry. */
if (len <= 1) return EINVAL;
if (len > NAME_MAX+1) return ENAMETOOLONG;
r = sys_safecopyfrom(m_in.m_source, grant, 0, (vir_bytes) name, len, D);
if (r != OK) return r;
if (name[len-1] != 0) {
printf("HGFS: VFS did not zero-terminate path component!\n");
return EINVAL;
}
return OK;
}
/*===========================================================================*
* do_noop *
*===========================================================================*/
PUBLIC int do_noop()
{
/* Generic handler for no-op system calls.
*/
return OK;
}
/*===========================================================================*
* no_sys *
*===========================================================================*/
PUBLIC int no_sys()
{
/* Generic handler for unimplemented system calls.
*/
return ENOSYS;
}

118
servers/hgfs/verify.c Normal file
View file

@ -0,0 +1,118 @@
/* This file contains routines that verify inodes and paths against the host.
*
* The entry points into this file are:
* verify_path check whether a path,inode pair is still valid
* verify_inode construct a path for an inode and verify the inode
* verify_dentry check a directory inode and look for a directory entry
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
/*===========================================================================*
* verify_path *
*===========================================================================*/
PUBLIC int verify_path(path, ino, attr, stale)
char path[PATH_MAX];
struct inode *ino;
struct hgfs_attr *attr;
int *stale;
{
/* Given a path, and the inode associated with that path, verify if the inode
* still matches the real world. Obtain the attributes of the file identified
* by the given path, and see if they match. If not, possibly mark the inode
* as deleted and return an error. Only upon success is the inode guaranteed
* to be usable.
*
* The caller must set the a_mask field of the passed attr struct.
* If 'stale' is not NULL, the value it points to must be initialized to 0,
* and will be set to 1 if the path was valid but the inode wasn't.
*/
int r;
attr->a_mask |= HGFS_ATTR_MODE;
r = hgfs_getattr(path, attr);
dprintf(("HGFS: verify_path: getattr('%s') returned %d\n", path, r));
if (r != OK) {
/* If we are told that the path does not exist, delete the inode */
if (r == ENOENT || r == ENOTDIR)
del_dentry(ino);
return r; /* path isn't valid */
}
/* If the file type (reg, dir) isn't what we thought, delete the inode */
if ((ino->i_flags & I_DIR) != MODE_TO_DIRFLAG(attr->a_mode)) {
del_dentry(ino);
if (stale != NULL) *stale = 1;
return ENOENT; /* path is valid, inode wasn't */
}
return OK; /* path and inode are valid */
}
/*===========================================================================*
* verify_inode *
*===========================================================================*/
PUBLIC int verify_inode(ino, path, attr)
struct inode *ino; /* inode to verify */
char path[PATH_MAX]; /* buffer in which to store the path */
struct hgfs_attr *attr; /* buffer for attributes, or NULL */
{
/* Given an inode, construct a path identifying the inode, and check whether
* that path is still valid for that inode (as far as we can tell). As a side
* effect, store attributes in the given attribute structure if not NULL (its
* a_mask member must then be set).
*/
struct hgfs_attr attr2;
int r;
if ((r = make_path(path, ino)) != OK) return r;
if (attr == NULL) {
attr2.a_mask = 0;
attr = &attr2;
}
return verify_path(path, ino, attr, NULL);
}
/*===========================================================================*
* verify_dentry *
*===========================================================================*/
PUBLIC int verify_dentry(parent, name, path, res_ino)
struct inode *parent; /* parent inode: the inode to verify */
char name[NAME_MAX+1]; /* the given directory entry path component */
char path[PATH_MAX]; /* buffer to store the resulting path in */
struct inode **res_ino; /* pointer for addressed inode (or NULL) */
{
/* Given a directory inode and a name, construct a path identifying that
* directory entry, check whether the path to the parent is still valid, and
* check whether there is an inode pointed to by the full path. Upon success,
* res_ino will contain either the inode for the full path, with increased
* refcount, or NIL_INODE if no such inode exists.
*/
int r;
if ((r = verify_inode(parent, path, NULL)) != OK)
return r;
dprintf(("HGFS: verify_dentry: given path is '%s', name '%s'\n", path,
name));
if ((r = push_path(path, name)) != OK)
return r;
dprintf(("HGFS: verify_dentry: path now '%s'\n", path));
*res_ino = lookup_dentry(parent, name);
return OK;
}

164
servers/hgfs/write.c Normal file
View file

@ -0,0 +1,164 @@
/* This file contains file writing system call handlers.
*
* The entry points into this file are:
* do_write perform the WRITE file system call
* do_ftrunc perform the FTRUNC file system call
*
* Created:
* April 2009 (D.C. van Moolenbroek)
*/
#include "inc.h"
FORWARD _PROTOTYPE( int write_file, (struct inode *ino, u64_t *posp,
size_t *countp, cp_grant_id_t *grantp) );
/*===========================================================================*
* write_file *
*===========================================================================*/
PRIVATE int write_file(ino, posp, countp, grantp)
struct inode *ino;
u64_t *posp;
size_t *countp;
cp_grant_id_t *grantp;
{
/* Write data or zeroes to a file, depending on whether a valid pointer to
* a data grant was provided.
*/
u64_t pos;
size_t count, size;
vir_bytes off;
char *ptr;
int r, chunk;
assert(!IS_DIR(ino));
if ((r = get_handle(ino)) != OK)
return r;
pos = *posp;
count = *countp;
assert(count > 0);
/* Use the buffer from libhgfs to eliminate extra copying. */
size = hgfs_writebuf(&ptr);
off = 0;
while (count > 0) {
chunk = MIN(count, size);
if (grantp != NULL) {
r = sys_safecopyfrom(m_in.m_source, *grantp,
off, (vir_bytes) ptr, chunk, D);
if (r != OK)
break;
} else {
/* Do this every time. We don't know what happens below. */
memset(ptr, 0, chunk);
}
if ((r = hgfs_write(ino->i_file, NULL, chunk, pos, FALSE)) <= 0)
break;
count -= r;
off += r;
pos = add64u(pos, r);
}
if (r < 0)
return r;
*posp = pos;
*countp = off;
return OK;
}
/*===========================================================================*
* do_write *
*===========================================================================*/
PUBLIC int do_write()
{
/* Write data to a file.
*/
struct inode *ino;
u64_t pos;
size_t count;
cp_grant_id_t grant;
int r;
if (state.read_only)
return EROFS;
if ((ino = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
return EINVAL;
if (IS_DIR(ino)) return EISDIR;
pos = make64(m_in.REQ_SEEK_POS_LO, m_in.REQ_SEEK_POS_HI);
count = m_in.REQ_NBYTES;
grant = m_in.REQ_GRANT;
if (count <= 0) return EINVAL;
if ((r = write_file(ino, &pos, &count, &grant)) != OK)
return r;
m_out.RES_SEEK_POS_HI = ex64hi(pos);
m_out.RES_SEEK_POS_LO = ex64lo(pos);
m_out.RES_NBYTES = count;
return OK;
}
/*===========================================================================*
* do_ftrunc *
*===========================================================================*/
PUBLIC int do_ftrunc()
{
/* Change file size or create file holes.
*/
char path[PATH_MAX];
struct inode *ino;
struct hgfs_attr attr;
u64_t start, end, delta;
size_t count;
int r;
if (state.read_only)
return EROFS;
if ((ino = find_inode(m_in.REQ_INODE_NR)) == NIL_INODE)
return EINVAL;
if (IS_DIR(ino)) return EISDIR;
start = make64(m_in.REQ_TRC_START_LO, m_in.REQ_TRC_START_HI);
end = make64(m_in.REQ_TRC_END_LO, m_in.REQ_TRC_END_HI);
if (cmp64u(end, 0) == 0) {
/* Truncate or expand the file. */
if ((r = verify_inode(ino, path, NULL)) != OK)
return r;
attr.a_mask = HGFS_ATTR_SIZE;
attr.a_size = start;
r = hgfs_setattr(path, &attr);
} else {
/* Write zeroes to the file. We can't create holes. */
if (cmp64(end, start) <= 0) return EINVAL;
delta = sub64(end, start);
if (ex64hi(delta) != 0) return EINVAL;
count = ex64lo(delta);
r = write_file(ino, &start, &count, NULL);
}
return r;
}