diff --git a/include/minix/hgfs.h b/include/minix/hgfs.h index 22dc09db4..06d630edb 100644 --- a/include/minix/hgfs.h +++ b/include/minix/hgfs.h @@ -26,22 +26,13 @@ struct hgfs_attr { #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_ATIME_SET 0x40 /* set specific file access time */ -#define HGFS_ATTR_MTIME_SET 0x80 /* set specific file modify time */ -#define HGFS_ATTR_ALL \ - (HGFS_ATTR_SIZE | HGFS_ATTR_CRTIME | HGFS_ATTR_ATIME | \ - HGFS_ATTR_MTIME | HGFS_ATTR_CTIME | HGFS_ATTR_MODE | \ - HGFS_ATTR_ATIME_SET | HGFS_ATTR_MTIME_SET) int hgfs_init(void); void hgfs_cleanup(void); -int hgfs_enabled(void); - int hgfs_open(char *path, int flags, int mode, hgfs_file_t *handle); int hgfs_read(hgfs_file_t handle, char *buf, size_t size, u64_t offset); -int hgfs_write(hgfs_file_t handle, const char *buf, size_t len, u64_t - offset, int append); +int hgfs_write(hgfs_file_t handle, char *buf, size_t len, u64_t offset); int hgfs_close(hgfs_file_t handle); size_t hgfs_readbuf(char **ptr); diff --git a/lib/libhgfs/Makefile b/lib/libhgfs/Makefile index da2ded5d3..d6d7a5292 100644 --- a/lib/libhgfs/Makefile +++ b/lib/libhgfs/Makefile @@ -2,6 +2,6 @@ LIB= hgfs SRCS= backdoor.S attr.c channel.c dir.c error.c file.c \ - link.c misc.c path.c rpc.c time.c + info.c link.c misc.c path.c rpc.c time.c .include diff --git a/lib/libhgfs/attr.c b/lib/libhgfs/attr.c index 85004d443..0043192b7 100644 --- a/lib/libhgfs/attr.c +++ b/lib/libhgfs/attr.c @@ -64,18 +64,34 @@ struct hgfs_attr *attr; { /* Set selected attributes of a file by path name. */ + u8_t mask; RPC_REQUEST(HGFS_REQ_SETATTR); - RPC_NEXT8 = (attr->a_mask & HGFS_ATTR_ALL); + /* This library implements the HGFS v1 protocol, which is largely + * path-oriented. This is the only method to set the file size, and thus, + * truncating a deleted file is not possible. This has been fixed in later + * HGFS protocol version (v2/v3). + */ + mask = 0; + if (attr->a_mask & HGFS_ATTR_MODE) mask |= HGFS_ATTR_MODE; + if (attr->a_mask & HGFS_ATTR_SIZE) mask |= HGFS_ATTR_SIZE; + if (attr->a_mask & HGFS_ATTR_CRTIME) mask |= HGFS_ATTR_CRTIME; + if (attr->a_mask & HGFS_ATTR_ATIME) + mask |= HGFS_ATTR_ATIME | HGFS_ATTR_ATIME_SET; + if (attr->a_mask & HGFS_ATTR_MTIME) + mask |= HGFS_ATTR_MTIME | HGFS_ATTR_MTIME_SET; + if (attr->a_mask & HGFS_ATTR_CTIME) mask |= HGFS_ATTR_CTIME; + + RPC_NEXT8 = mask; 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_SET) ? &attr->a_atime : NULL); - time_put((attr->a_mask & HGFS_ATTR_MTIME_SET) ? &attr->a_mtime : 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); diff --git a/lib/libhgfs/const.h b/lib/libhgfs/const.h index 7e4772481..b679fde3d 100644 --- a/lib/libhgfs/const.h +++ b/lib/libhgfs/const.h @@ -60,3 +60,13 @@ enum { /* 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) + +/* HGFS attribute flags */ +#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_ATIME_SET 0x40 /* set specific file access time */ +#define HGFS_ATTR_MTIME_SET 0x80 /* set specific file modify time */ diff --git a/lib/libhgfs/dir.c b/lib/libhgfs/dir.c index f2a220f2a..c73bf52c2 100644 --- a/lib/libhgfs/dir.c +++ b/lib/libhgfs/dir.c @@ -39,7 +39,7 @@ struct hgfs_attr *attr; * 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. + * undefined. ENOENT is returned upon end of directory. */ int r; @@ -47,12 +47,20 @@ struct hgfs_attr *attr; RPC_NEXT32 = (u32_t)handle; RPC_NEXT32 = index; + /* EINVAL signifies end of directory. */ if ((r = rpc_query()) != OK) - return r; + return (r == EINVAL) ? ENOENT : OK; attr_get(attr); - return path_get(buf, size); + if ((r = path_get(buf, size)) != OK) + return r; + + /* 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.. + */ + return (!buf[0]) ? ENOENT : OK; } /*===========================================================================* diff --git a/lib/libhgfs/file.c b/lib/libhgfs/file.c index fb3447b22..6fdeeb1b4 100644 --- a/lib/libhgfs/file.c +++ b/lib/libhgfs/file.c @@ -74,8 +74,8 @@ u64_t off; /* file offset */ len = RPC_NEXT32; if (len > max) len = max; /* sanity check */ - /* Only copy out data if we're requested to do so. */ - if (buf != NULL) + /* Only copy out data if we're not operating directly on the RPC buffer. */ + if (buf != RPC_PTR) memcpy(buf, RPC_PTR, len); return len; @@ -84,12 +84,11 @@ u64_t off; /* file offset */ /*===========================================================================* * hgfs_write * *===========================================================================*/ -int hgfs_write(handle, buf, len, off, append) +int hgfs_write(handle, buf, len, off) hgfs_file_t handle; /* handle to open file */ -const char *buf; /* data buffer or NULL */ +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. */ @@ -97,22 +96,13 @@ int append; /* if set, append to file (ignore offset) */ 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_NEXT8 = 0; /* append flag */ + 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) + /* Only copy in data if we're not operating directly on the RPC buffer. */ + if (RPC_PTR != buf) memcpy(RPC_PTR, buf, len); RPC_ADVANCE(len); diff --git a/lib/libhgfs/info.c b/lib/libhgfs/info.c new file mode 100644 index 000000000..f6d9258e2 --- /dev/null +++ b/lib/libhgfs/info.c @@ -0,0 +1,38 @@ +/* Part of libhgfs - (c) 2009, D.C. van Moolenbroek */ + +#include "inc.h" + +/*===========================================================================* + * hgfs_queryvol * + *===========================================================================*/ +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); + + /* It appears that this call always fails with EACCES ("permission denied") + * on read-only folders. As far as I can tell, this is a VMware bug. + */ + 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; +} diff --git a/lib/libhgfs/misc.c b/lib/libhgfs/misc.c index 9ddb4231c..d81c5c71e 100644 --- a/lib/libhgfs/misc.c +++ b/lib/libhgfs/misc.c @@ -8,8 +8,7 @@ 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). + * otherwise. If EAGAIN is returned, shared folders are disabled. */ time_init(); @@ -27,47 +26,3 @@ void hgfs_cleanup() rpc_close(); } - -/*===========================================================================* - * hgfs_enabled * - *===========================================================================*/ -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 * - *===========================================================================*/ -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; -} diff --git a/lib/libhgfs/rpc.c b/lib/libhgfs/rpc.c index 08a6655d9..479e8caf8 100644 --- a/lib/libhgfs/rpc.c +++ b/lib/libhgfs/rpc.c @@ -13,7 +13,8 @@ static struct channel rpc_chan; 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. + * is working. Return OK upon success, or a negative error code otherwise; in + * particular, return EAGAIN if shared folders are disabled. */ int r; @@ -22,7 +23,7 @@ int rpc_open() r = rpc_test(); - if (r != OK && r != EAGAIN) + if (r != OK) channel_close(&rpc_chan); return r; diff --git a/servers/hgfs/const.h b/servers/hgfs/const.h index c757d3c8c..9b1bf337f 100644 --- a/servers/hgfs/const.h +++ b/servers/hgfs/const.h @@ -7,6 +7,6 @@ #define NUM_HASH_SLOTS 1023 /* Arbitrary block size constant returned by fstatfs and statvfs. - * Also used by getdents. This is not the actual HGFS data transfer unit size. + * Also used by getdents. This is not the underlying data transfer unit size. */ #define BLOCK_SIZE 4096 diff --git a/servers/hgfs/inode.h b/servers/hgfs/inode.h index 12f6b4b06..42c820943 100644 --- a/servers/hgfs/inode.h +++ b/servers/hgfs/inode.h @@ -20,10 +20,11 @@ * 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. + * because of open file handles, but cannot be referenced by path anymore. The + * underlying protocol may not support truncation of open files anyway. Since + * we currently cannot guarantee that a file is actually opened before it is + * deleted (as this would consistute opening every file being looked up), we + * effectively do not properly support open deleted files at all anyway. * * An inode is REFERENCED iff it has a reference count > 0 *or* has children. * An inode is LINKED IN iff it has a parent. @@ -62,8 +63,8 @@ struct inode { 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 */ + hgfs_file_t u_file; /* handle to open file */ + hgfs_dir_t u_dir; /* handle to open directory */ } i_u; char i_name[NAME_MAX+1]; /* entry name in parent directory */ }; diff --git a/servers/hgfs/main.c b/servers/hgfs/main.c index a0c94360f..7c2b7b93a 100644 --- a/servers/hgfs/main.c +++ b/servers/hgfs/main.c @@ -60,8 +60,11 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info)) /* 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); + if (r != OK) { + if (r == EAGAIN) + printf("HGFS: shared folders are disabled\n"); + else + printf("HGFS: unable to initialize HGFS library (%d)\n", r); return r; } diff --git a/servers/hgfs/misc.c b/servers/hgfs/misc.c index 7135941dc..ad9aca32d 100644 --- a/servers/hgfs/misc.c +++ b/servers/hgfs/misc.c @@ -43,7 +43,8 @@ int do_statvfs() /* Unfortunately, we cannot be any more specific than this, because we are * not given an inode number. Statistics of individual shared folders can - * only be obtained by using the "prefix=" option when mounting. + * only be obtained by making sure that the root of the file system is an + * actual share, and not a list of available shares. */ if ((ino = find_inode(ROOT_INODE_NR)) == NULL) return EINVAL; @@ -51,9 +52,6 @@ int do_statvfs() if ((r = verify_inode(ino, path, NULL)) != OK) return r; - /* It appears that this call always fails with EACCES ("permission denied") - * on read-only folders. As far as I can tell, this is a VMware bug. - */ if ((r = hgfs_queryvol(path, &free, &total)) != OK) return r; diff --git a/servers/hgfs/mount.c b/servers/hgfs/mount.c index ab154fe38..729ea01f6 100644 --- a/servers/hgfs/mount.c +++ b/servers/hgfs/mount.c @@ -44,9 +44,7 @@ int do_readsuper() * 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)) + 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"); diff --git a/servers/hgfs/read.c b/servers/hgfs/read.c index 63c2313a2..4f0eae527 100644 --- a/servers/hgfs/read.c +++ b/servers/hgfs/read.c @@ -48,7 +48,7 @@ int do_read() while (count > 0) { chunk = MIN(count, size); - if ((r = hgfs_read(ino->i_file, NULL, chunk, pos)) <= 0) + if ((r = hgfs_read(ino->i_file, ptr, chunk, pos)) <= 0) break; chunk = r; @@ -117,7 +117,7 @@ int do_getdents() */ 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 "..", + * We have no idea whether the host will give us "." and/or "..", * so generate our own and skip those from the host. */ if (pos == 0) { @@ -144,14 +144,9 @@ int do_getdents() r = hgfs_readdir(ino->i_dir, pos - 2, name, sizeof(name), &attr); - if (r != OK || !name[0]) { + if (r != OK) { /* 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]) { + if (r == ENOENT) { put_handle(ino); break; diff --git a/servers/hgfs/stat.c b/servers/hgfs/stat.c index cdb6d5661..eb0a5dec3 100644 --- a/servers/hgfs/stat.c +++ b/servers/hgfs/stat.c @@ -19,7 +19,7 @@ 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. +/* Return the mode for an inode, given the inode and the retrieved mode. */ mode &= S_IRWXU; @@ -31,7 +31,7 @@ int mode; mode = S_IFREG | (mode & opt.file_mask); if (state.read_only) - mode &= ~0222; + mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); return mode; } @@ -158,8 +158,7 @@ int do_utime() if ((r = verify_inode(ino, path, NULL)) != OK) return r; - attr.a_mask = HGFS_ATTR_ATIME | HGFS_ATTR_MTIME | HGFS_ATTR_ATIME_SET | - HGFS_ATTR_MTIME_SET; + attr.a_mask = HGFS_ATTR_ATIME | HGFS_ATTR_MTIME; attr.a_atime.tv_sec = m_in.REQ_ACTIME; attr.a_atime.tv_nsec = 0; attr.a_mtime.tv_sec = m_in.REQ_MODTIME; diff --git a/servers/hgfs/write.c b/servers/hgfs/write.c index 5fd5780d1..ab21bba2a 100644 --- a/servers/hgfs/write.c +++ b/servers/hgfs/write.c @@ -59,7 +59,7 @@ cp_grant_id_t *grantp; memset(ptr, 0, chunk); } - if ((r = hgfs_write(ino->i_file, NULL, chunk, pos, FALSE)) <= 0) + if ((r = hgfs_write(ino->i_file, ptr, chunk, pos)) <= 0) break; count -= r;