Reorganize some directories
servers/hgfs/hgfs_server => servers/hgfs servers/hgfs/libhgfs => lib/libhgfs servers/rs/service => commands/service drivers/memory/memory_driver => drivers/memory drivers/memory/ramdisk => drivers/ramdisk
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
.include <minix.own.mk>
|
||||
|
||||
SUBDIR= csu libc libcurses libdriver libnetdriver libend libedit libm libsys \
|
||||
libtimers libutil libbz2 libl
|
||||
libtimers libutil libbz2 libl libhgfs
|
||||
|
||||
.if ${COMPILER_TYPE} == "ack"
|
||||
SUBDIR+= ack/libd ack/libe ack/libfp ack/liby
|
||||
|
||||
7
lib/libhgfs/Makefile
Normal file
7
lib/libhgfs/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
# Makefile for HGFS library
|
||||
|
||||
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
|
||||
|
||||
.include <minix.lib.mk>
|
||||
86
lib/libhgfs/attr.c
Normal file
86
lib/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
lib/libhgfs/backdoor.S
Normal file
106
lib/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
lib/libhgfs/channel.c
Normal file
146
lib/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
lib/libhgfs/const.h
Normal file
62
lib/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
lib/libhgfs/dir.c
Normal file
71
lib/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
lib/libhgfs/error.c
Normal file
40
lib/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
lib/libhgfs/file.c
Normal file
182
lib/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
lib/libhgfs/glo.h
Normal file
6
lib/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;
|
||||
19
lib/libhgfs/inc.h
Normal file
19
lib/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
lib/libhgfs/link.c
Normal file
73
lib/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
lib/libhgfs/misc.c
Normal file
73
lib/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
lib/libhgfs/path.c
Normal file
73
lib/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
lib/libhgfs/proto.h
Normal file
51
lib/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
lib/libhgfs/rpc.c
Normal file
94
lib/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
lib/libhgfs/time.c
Normal file
69
lib/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
lib/libhgfs/type.h
Normal file
7
lib/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;
|
||||
};
|
||||
Reference in New Issue
Block a user