HGFS - VMware Shared Folders file system server
This commit is contained in:
parent
4c2cb6c04f
commit
71fe2852f4
46 changed files with 4400 additions and 0 deletions
|
@ -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
76
man/man8/hgfs.8
Normal 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>
|
|
@ -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
34
servers/hgfs/Makefile
Normal 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
13
servers/hgfs/const.h
Normal 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
194
servers/hgfs/dentry.c
Normal 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
12
servers/hgfs/glo.h
Normal 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
78
servers/hgfs/handle.c
Normal 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
37
servers/hgfs/inc.h
Normal 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
299
servers/hgfs/inode.c
Normal 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
93
servers/hgfs/inode.h
Normal 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 */
|
26
servers/hgfs/libhgfs/Makefile
Normal file
26
servers/hgfs/libhgfs/Makefile
Normal 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
|
86
servers/hgfs/libhgfs/attr.c
Normal file
86
servers/hgfs/libhgfs/attr.c
Normal 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();
|
||||
}
|
106
servers/hgfs/libhgfs/backdoor.S
Normal file
106
servers/hgfs/libhgfs/backdoor.S
Normal 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
|
146
servers/hgfs/libhgfs/channel.c
Normal file
146
servers/hgfs/libhgfs/channel.c
Normal 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;
|
||||
}
|
62
servers/hgfs/libhgfs/const.h
Normal file
62
servers/hgfs/libhgfs/const.h
Normal 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)
|
71
servers/hgfs/libhgfs/dir.c
Normal file
71
servers/hgfs/libhgfs/dir.c
Normal 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();
|
||||
}
|
40
servers/hgfs/libhgfs/error.c
Normal file
40
servers/hgfs/libhgfs/error.c
Normal 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
182
servers/hgfs/libhgfs/file.c
Normal 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;
|
||||
}
|
6
servers/hgfs/libhgfs/glo.h
Normal file
6
servers/hgfs/libhgfs/glo.h
Normal 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;
|
63
servers/hgfs/libhgfs/hgfs.h
Normal file
63
servers/hgfs/libhgfs/hgfs.h
Normal 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 */
|
19
servers/hgfs/libhgfs/inc.h
Normal file
19
servers/hgfs/libhgfs/inc.h
Normal 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"
|
73
servers/hgfs/libhgfs/link.c
Normal file
73
servers/hgfs/libhgfs/link.c
Normal 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();
|
||||
}
|
73
servers/hgfs/libhgfs/misc.c
Normal file
73
servers/hgfs/libhgfs/misc.c
Normal 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;
|
||||
}
|
73
servers/hgfs/libhgfs/path.c
Normal file
73
servers/hgfs/libhgfs/path.c
Normal 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;
|
||||
}
|
51
servers/hgfs/libhgfs/proto.h
Normal file
51
servers/hgfs/libhgfs/proto.h
Normal 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) );
|
94
servers/hgfs/libhgfs/rpc.c
Normal file
94
servers/hgfs/libhgfs/rpc.c
Normal 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);
|
||||
}
|
69
servers/hgfs/libhgfs/time.c
Normal file
69
servers/hgfs/libhgfs/time.c
Normal 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);
|
||||
}
|
7
servers/hgfs/libhgfs/type.h
Normal file
7
servers/hgfs/libhgfs/type.h
Normal 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
403
servers/hgfs/link.c
Normal 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
333
servers/hgfs/lookup.c
Normal 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
232
servers/hgfs/main.c
Normal 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
27
servers/hgfs/misc.c
Normal 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
94
servers/hgfs/mount.c
Normal 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
56
servers/hgfs/name.c
Normal 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
128
servers/hgfs/optset.c
Normal 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
30
servers/hgfs/optset.h
Normal 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
112
servers/hgfs/path.c
Normal 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
82
servers/hgfs/proto.h
Normal 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
239
servers/hgfs/read.c
Normal 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
154
servers/hgfs/stat.c
Normal 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
46
servers/hgfs/table.c
Normal 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
19
servers/hgfs/type.h
Normal 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
63
servers/hgfs/util.c
Normal 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
118
servers/hgfs/verify.c
Normal 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
164
servers/hgfs/write.c
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue