Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save skroll/b0479a9a12f41914fe31 to your computer and use it in GitHub Desktop.
Save skroll/b0479a9a12f41914fe31 to your computer and use it in GitHub Desktop.
Patches to get open-vm-tools-2013.09.16-1328054 vmhgfs kernel module to compile and work on 3.15
diff --git a/modules/linux/shared/compat_dcache.h b/modules/linux/shared/compat_dcache.h
index 0450aee..a38c861 100644
--- a/modules/linux/shared/compat_dcache.h
+++ b/modules/linux/shared/compat_dcache.h
@@ -48,4 +48,15 @@
})
#endif
+/*
+ * d_count field was removed in 3.11.0.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+#define compat_d_count(dentry) d_count(dentry)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
+#define compat_d_count(dentry) dentry->d_count
+#else
+#define compat_d_count(dentry) atomic_read(&dentry->d_count);
+#endif
+
#endif /* __COMPAT_DCACHE_H__ */
--
2.0.1
diff --git a/modules/linux/shared/autoconf/file_operations_flush.c b/modules/linux/shared/autoconf/file_operations_flush.c
new file mode 100644
index 0000000..818aee3
--- /dev/null
+++ b/modules/linux/shared/autoconf/file_operations_flush.c
@@ -0,0 +1,46 @@
+/*********************************************************
+ * Copyright (C) 2013 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*
+ * Linux v2.6.18 added an owner parameter to flush.
+ * But SLES10 has backported the change to its 2.6.16.60 kernel,
+ * so we can't rely solely on kernel version to determine number of
+ * arguments.
+ *
+ * This test will fail on a kernel with such a patch.
+ */
+
+#include "compat_version.h"
+#include "compat_autoconf.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+#error This compile test intentionally fails on 2.6.18 and newer kernels.
+#else
+
+#include <linux/fs.h>
+
+static int TestFlush(struct file *file);
+{
+ return 0;
+}
+
+struct file_operations testFO = {
+ .flush = TestFlush,
+};
+
+#endif
--
2.0.1
diff --git a/modules/linux/vmhgfs/Makefile.kernel b/modules/linux/vmhgfs/Makefile.kernel
index a8a2caa..d5daae6 100644
--- a/modules/linux/vmhgfs/Makefile.kernel
+++ b/modules/linux/vmhgfs/Makefile.kernel
@@ -28,6 +28,7 @@ EXTRA_CFLAGS := $(CC_OPTS) $(INCLUDE)
EXTRA_CFLAGS += $(call vm_check_build, $(AUTOCONF_DIR)/cachector.c, -DVMW_KMEMCR_CTOR_HAS_3_ARGS, )
EXTRA_CFLAGS += $(call vm_check_build, $(AUTOCONF_DIR)/cachector1.c, -DVMW_KMEMCR_CTOR_HAS_2_ARGS, )
EXTRA_CFLAGS += $(call vm_check_build, $(AUTOCONF_DIR)/file_operations_fsync.c, -DVMW_FSYNC_31, )
+EXTRA_CFLAGS += $(call vm_check_build, $(AUTOCONF_DIR)/file_operations_flush.c, -DVMW_FLUSH_HAS_1_ARG, )
# Note: These tests are inverted
EXTRA_CFLAGS += $(call vm_check_build, $(AUTOCONF_DIR)/getsb1.c,, -DVMW_GETSB_2618)
diff --git a/modules/linux/vmhgfs/dir.c b/modules/linux/vmhgfs/dir.c
index e4e82b4..c892593 100644
--- a/modules/linux/vmhgfs/dir.c
+++ b/modules/linux/vmhgfs/dir.c
@@ -1157,7 +1157,8 @@ HgfsDoReaddir(struct file *file, // IN:
Bool dotAndDotDotIgnore, // IN: ignore "." and ".."
filldir_t filldirCb, // IN: system filler callback
void *filldirCtx, // IN/OUT: system filler context
- loff_t *entryPos) // IN/OUT: entry position
+ loff_t *fillPos, // IN/OUT: fill entry position
+ loff_t *currentPos) // IN/OUT: current position
{
char *entryName = NULL; // buf for escaped version of name
size_t entryNameBufLen = NAME_MAX + 1;
@@ -1179,7 +1180,7 @@ HgfsDoReaddir(struct file *file, // IN:
__func__,
file->f_dentry->d_name.name,
file->f_dentry->d_inode->i_ino,
- *entryPos));
+ *currentPos));
/*
* Refresh entries if required. See rm -rf 6.10+ breaking issue.
@@ -1189,7 +1190,6 @@ HgfsDoReaddir(struct file *file, // IN:
return result;
}
-
/*
* Some day when we're out of things to do we can move this to a slab
* allocator.
@@ -1207,7 +1207,7 @@ HgfsDoReaddir(struct file *file, // IN:
uint32 entryType = DT_UNKNOWN;
result = HgfsReaddirNextEntry(file,
- *entryPos,
+ *currentPos,
dotAndDotDotIgnore,
entryNameBufLen,
entryName,
@@ -1228,20 +1228,20 @@ HgfsDoReaddir(struct file *file, // IN:
}
if (entryIgnore) {
- *entryPos += 1;
+ *currentPos += 1;
continue;
}
/*
* Call the HGFS wrapper to the system fill function to set this dentry.
*/
- LOG(6, (KERN_DEBUG "VMware hgfs: %s: dir_emit(%s, %u, %Lu)\n",
- __func__, entryName, entryNameLength, *entryPos));
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: dir_emit(%s, %u, @ (fill %Lu HGFS %Lu)\n",
+ __func__, entryName, entryNameLength, *fillPos, *currentPos));
if (!HgfsReaddirFillEntry(filldirCb, /* filldir callback function */
filldirCtx, /* filldir callback struct */
entryName, /* name of dirent */
entryNameLength, /* length of name */
- *entryPos, /* entry position */
+ *fillPos, /* fill entry position */
entryIno, /* inode number (0 makes it not show) */
entryType)) { /* type of dirent */
/*
@@ -1254,7 +1254,8 @@ HgfsDoReaddir(struct file *file, // IN:
result = 0;
break;
}
- *entryPos += 1;
+ *currentPos += 1;
+ *fillPos += 1;
}
LOG(6, (KERN_DEBUG "VMware hgfs: %s: return\n",__func__));
@@ -1284,6 +1285,12 @@ static int
HgfsReaddir(struct file *file, // IN:
struct dir_context *ctx) // IN:
{
+ HgfsFileInfo *fInfo = FILE_GET_FI_P(file);
+
+ if (0 == ctx->pos) {
+ fInfo->direntPos = 0;
+ }
+
/* If either dot and dotdot are filled in for us we can exit. */
if (!dir_emit_dots(file, ctx)) {
LOG(6, (KERN_DEBUG "VMware hgfs: %s: dir_emit_dots(%s, @ %Lu)\n",
@@ -1292,7 +1299,7 @@ HgfsReaddir(struct file *file, // IN:
}
/* It is sufficient to pass the context as it contains the filler function. */
- return HgfsDoReaddir(file, TRUE, NULL, ctx, &ctx->pos);
+ return HgfsDoReaddir(file, TRUE, NULL, ctx, &ctx->pos, &fInfo->direntPos);
}
@@ -1367,7 +1374,13 @@ HgfsReaddir(struct file *file, // IN: Directory to read from
void *dirent, // OUT: Buffer to copy dentries into
filldir_t filldir) // IN: Filler function
{
- return HgfsDoReaddir(file, FALSE, filldir, dirent, &file->f_pos);
+ HgfsFileInfo *fInfo = FILE_GET_FI_P(file);
+
+ if (0 == file->f_pos) {
+ fInfo->direntPos = 0;
+ }
+
+ return HgfsDoReaddir(file, FALSE, filldir, dirent, &file->f_pos, &fInfo->direntPos);
}
diff --git a/modules/linux/vmhgfs/file.c b/modules/linux/vmhgfs/file.c
index 3ddbfef..ba1a5e4 100644
--- a/modules/linux/vmhgfs/file.c
+++ b/modules/linux/vmhgfs/file.c
@@ -47,6 +47,12 @@
#include "vm_assert.h"
#include "vm_basic_types.h"
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)
+#define HGFS_FILECTL_SYNC(flags) ((flags) & O_DSYNC)
+#else
+#define HGFS_FILECTL_SYNC(flags) ((flags) & O_SYNC)
+#endif
+
/* Private functions. */
static int HgfsPackOpenRequest(struct inode *inode,
struct file *file,
@@ -56,6 +62,7 @@ static int HgfsUnpackOpenReply(HgfsReq *req,
HgfsOp opUsed,
HgfsHandle *file,
HgfsLockType *lock);
+static int HgfsGetOpenFlags(uint32 flags);
/* HGFS file operations for files. */
static int HgfsOpen(struct inode *inode,
@@ -84,6 +91,16 @@ static loff_t HgfsSeek(struct file *file,
loff_t offset,
int origin);
+static int HgfsFlush(struct file *file
+#if !defined VMW_FLUSH_HAS_1_ARG
+ ,fl_owner_t id
+#endif
+ );
+
+#if !defined VMW_FSYNC_31
+static int HgfsDoFsync(struct inode *inode);
+#endif
+
static int HgfsFsync(struct file *file,
#if defined VMW_FSYNC_OLD
struct dentry *dentry,
@@ -125,7 +142,10 @@ struct file_operations HgfsFileFileOperations = {
.owner = THIS_MODULE,
.open = HgfsOpen,
.llseek = HgfsSeek,
+ .flush = HgfsFlush,
#if defined VMW_USE_AIO
+ .read = do_sync_read,
+ .write = do_sync_write,
.aio_read = HgfsAioRead,
.aio_write = HgfsAioWrite,
#else
@@ -433,6 +453,95 @@ HgfsUnpackOpenReply(HgfsReq *req, // IN: Packet with reply inside
/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsGetOpenFlags --
+ *
+ * Based on the flags requested by the process making the open()
+ * syscall, determine which flags to send to the server to open the
+ * file.
+ *
+ * Results:
+ * Returns the correct HgfsOpenFlags enumeration to send to the
+ * server, or -1 on failure.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+HgfsGetOpenFlags(uint32 flags) // IN: Open flags
+{
+ uint32 mask = O_CREAT | O_TRUNC | O_EXCL;
+ int result = -1;
+
+ LOG(6, (KERN_DEBUG "VMware hgfs: HgfsGetOpenFlags: entered\n"));
+
+ /*
+ * Mask the flags to only look at O_CREAT, O_EXCL, and O_TRUNC.
+ */
+
+ flags &= mask;
+
+ /* O_EXCL has no meaning if O_CREAT is not set. */
+ if (!(flags & O_CREAT)) {
+ flags &= ~O_EXCL;
+ }
+
+ /* Pick the right HgfsOpenFlags. */
+ switch (flags) {
+
+ case 0:
+ /* Regular open; fails if file nonexistant. */
+ result = HGFS_OPEN;
+ break;
+
+ case O_CREAT:
+ /* Create file; if it exists already just open it. */
+ result = HGFS_OPEN_CREATE;
+ break;
+
+ case O_TRUNC:
+ /* Truncate existing file; fails if nonexistant. */
+ result = HGFS_OPEN_EMPTY;
+ break;
+
+ case (O_CREAT | O_EXCL):
+ /* Create file; fail if it exists already. */
+ result = HGFS_OPEN_CREATE_SAFE;
+ break;
+
+ case (O_CREAT | O_TRUNC):
+ /* Create file; if it exists already, truncate it. */
+ result = HGFS_OPEN_CREATE_EMPTY;
+ break;
+
+ default:
+ /*
+ * This can only happen if all three flags are set, which
+ * conceptually makes no sense because O_EXCL and O_TRUNC are
+ * mutually exclusive if O_CREAT is set.
+ *
+ * However, the open(2) man page doesn't say you can't set all
+ * three flags, and certain apps (*cough* Nautilus *cough*) do
+ * so. To be friendly to those apps, we just silenty drop the
+ * O_TRUNC flag on the assumption that it's safer to honor
+ * O_EXCL.
+ */
+ LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetOpenFlags: invalid open "
+ "flags %o. Ignoring the O_TRUNC flag.\n", flags));
+ result = HGFS_OPEN_CREATE_SAFE;
+ break;
+ }
+
+ return result;
+}
+
+
+
+/*
* HGFS file operations for files.
*/
@@ -665,21 +774,15 @@ HgfsAioRead(struct kiocb *iocb, // IN: I/O control block
loff_t offset) // IN: Offset at which to read
{
int result;
- struct dentry *readDentry;
ASSERT(iocb);
ASSERT(iocb->ki_filp);
ASSERT(iocb->ki_filp->f_dentry);
ASSERT(iov);
- readDentry = iocb->ki_filp->f_dentry;
-
- LOG(4, (KERN_DEBUG "VMware hgfs: %s(%s/%s, %lu@%lu)\n",
- __func__, readDentry->d_parent->d_name.name,
- readDentry->d_name.name,
- (unsigned long) iov_length(iov, numSegs), (unsigned long) offset));
+ LOG(6, (KERN_DEBUG "VMware hgfs: HgfsAioRead: was called\n"));
- result = HgfsRevalidate(readDentry);
+ result = HgfsRevalidate(iocb->ki_filp->f_dentry);
if (result) {
LOG(4, (KERN_DEBUG "VMware hgfs: HgfsAioRead: invalid dentry\n"));
goto out;
@@ -721,6 +824,7 @@ HgfsAioWrite(struct kiocb *iocb, // IN: I/O control block
{
int result;
struct dentry *writeDentry;
+ HgfsInodeInfo *iinfo;
ASSERT(iocb);
ASSERT(iocb->ki_filp);
@@ -728,12 +832,27 @@ HgfsAioWrite(struct kiocb *iocb, // IN: I/O control block
ASSERT(iov);
writeDentry = iocb->ki_filp->f_dentry;
+ iinfo = INODE_GET_II_P(writeDentry->d_inode);
LOG(4, (KERN_DEBUG "VMware hgfs: %s(%s/%s, %lu@%Ld)\n",
__func__, writeDentry->d_parent->d_name.name,
writeDentry->d_name.name,
(unsigned long) iov_length(iov, numSegs), (long long) offset));
+ spin_lock(&writeDentry->d_inode->i_lock);
+ /*
+ * Guard against dentry revalidation invalidating the inode underneath us.
+ *
+ * Data is being written and may have valid data in a page in the cache.
+ * This action prevents any invalidating of the inode when a flushing of
+ * cache data occurs prior to syncing the file with the server's attributes.
+ * The flushing of cache data would empty our in memory write pages list and
+ * would cause the inode modified write time to be updated and so the inode
+ * would also be invalidated.
+ */
+ iinfo->numWbPages++;
+ spin_unlock(&writeDentry->d_inode->i_lock);
+
result = HgfsRevalidate(writeDentry);
if (result) {
LOG(4, (KERN_DEBUG "VMware hgfs: HgfsAioWrite: invalid dentry\n"));
@@ -741,7 +860,27 @@ HgfsAioWrite(struct kiocb *iocb, // IN: I/O control block
}
result = generic_file_aio_write(iocb, iov, numSegs, offset);
- out:
+
+ if (result >= 0) {
+ if (IS_SYNC(writeDentry->d_inode) ||
+ HGFS_FILECTL_SYNC(iocb->ki_filp->f_flags)) {
+ int error;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
+ error = vfs_fsync(iocb->ki_filp, 0);
+#else
+ error = HgfsDoFsync(writeDentry->d_inode);
+#endif
+
+ if (error < 0) {
+ result = error;
+ }
+ }
+ }
+
+out:
+ spin_lock(&writeDentry->d_inode->i_lock);
+ iinfo->numWbPages--;
+ spin_unlock(&writeDentry->d_inode->i_lock);
return result;
}
@@ -895,6 +1034,100 @@ HgfsSeek(struct file *file, // IN: File to seek
return result;
}
+#if !defined VMW_FSYNC_31
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsDoFsync --
+ *
+ * Helper for HgfsFlush() and HgfsFsync().
+ *
+ * The hgfs protocol doesn't support fsync explicityly yet.
+ * So for now, we flush all the pages to presumably honor the
+ * intent of an app calling fsync() which is to get the
+ * data onto persistent storage. As things stand now we're at
+ * the whim of the hgfs server code running on the host to fsync or
+ * not if and when it pleases.
+ *
+ *
+ * Results:
+ * Returns zero on success. Otherwise an error.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+HgfsDoFsync(struct inode *inode) // IN: File we operate on
+{
+ int ret;
+
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s(%"FMT64"u)\n",
+ __func__, INODE_GET_II_P(inode)->hostFileId));
+
+ ret = compat_filemap_write_and_wait(inode->i_mapping);
+
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: returns %d\n",
+ __func__, ret));
+
+ return ret;
+}
+#endif
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsFlush --
+ *
+ * Called when user process calls fflush() on an hgfs file.
+ * Flush all dirty pages and check for write errors.
+ *
+ *
+ * Results:
+ * Returns zero on success. (Currently always succeeds).
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+HgfsFlush(struct file *file // IN: file to flush
+#if !defined VMW_FLUSH_HAS_1_ARG
+ ,fl_owner_t id // IN: id not used
+#endif
+ )
+{
+ int ret = 0;
+
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s(%s/%s)\n",
+ __func__, file->f_dentry->d_parent->d_name.name,
+ file->f_dentry->d_name.name));
+
+ if ((file->f_mode & FMODE_WRITE) == 0) {
+ goto exit;
+ }
+
+
+ /* Flush writes to the server and return any errors */
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: calling vfs_sync ... \n",
+ __func__));
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
+ ret = vfs_fsync(file, 0);
+#else
+ ret = HgfsDoFsync(file->f_dentry->d_inode);
+#endif
+
+exit:
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: returns %d\n",
+ __func__, ret));
+ return ret;
+}
+
/*
*----------------------------------------------------------------------
@@ -903,21 +1136,13 @@ HgfsSeek(struct file *file, // IN: File to seek
*
* Called when user process calls fsync() on hgfs file.
*
- * The hgfs protocol doesn't support fsync yet, so for now, we punt
- * and just return success. This is a little less sketchy than it
- * might sound, because hgfs skips the buffer cache in the guest
- * anyway (we always write to the host immediately).
- *
- * In the future we might want to try harder though, since
- * presumably the intent of an app calling fsync() is to get the
+ * The hgfs protocol doesn't support fsync explicitly yet,
+ * so for now, we flush all the pages to presumably honor the
+ * intent of an app calling fsync() which is to get the
* data onto persistent storage, and as things stand now we're at
* the whim of the hgfs server code running on the host to fsync or
* not if and when it pleases.
*
- * Note that do_fsync will call filemap_fdatawrite() before us and
- * filemap_fdatawait() after us, so there's no need to do anything
- * here w.r.t. writing out dirty pages.
- *
* Results:
* Returns zero on success. (Currently always succeeds).
*
@@ -937,18 +1162,37 @@ HgfsFsync(struct file *file, // IN: File we operate on
#endif
int datasync) // IN: fdatasync or fsync
{
- LOG(6, (KERN_DEBUG "VMware hgfs: %s(%s/%s, %lld, %lld, %d)\n",
+ int ret = 0;
+ loff_t startRange;
+ loff_t endRange;
+ struct inode *inode;
+
+#if defined VMW_FSYNC_31
+ startRange = start;
+ endRange = end;
+#else
+ startRange = 0;
+ endRange = MAX_INT64;
+#endif
+
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s(%s/%s, %lld, %lld, %d)\n",
__func__,
file->f_dentry->d_parent->d_name.name,
file->f_dentry->d_name.name,
+ startRange, endRange,
+ datasync));
+
+ /* Flush writes to the server and return any errors */
+ inode = file->f_dentry->d_inode;
#if defined VMW_FSYNC_31
- start, end,
+ ret = filemap_write_and_wait_range(inode->i_mapping, startRange, endRange);
#else
- (loff_t)0, (loff_t)0,
+ ret = HgfsDoFsync(inode);
#endif
- datasync));
- return 0;
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: written pages %lld, %lld returns %d)\n",
+ __func__, startRange, endRange, ret));
+ return ret;
}
diff --git a/modules/linux/vmhgfs/filesystem.c b/modules/linux/vmhgfs/filesystem.c
index f101ca7..dc0adcd 100644
--- a/modules/linux/vmhgfs/filesystem.c
+++ b/modules/linux/vmhgfs/filesystem.c
@@ -83,7 +83,6 @@ HgfsOp hgfsVersionCreateSymlink;
static inline unsigned long HgfsComputeBlockBits(unsigned long blockSize);
static compat_kmem_cache_ctor HgfsInodeCacheCtor;
static HgfsSuperInfo *HgfsInitSuperInfo(HgfsMountInfo *mountInfo);
-static int HgfsGetRootDentry(struct super_block *sb, struct dentry **rootDentry);
static int HgfsReadSuper(struct super_block *sb,
void *rawData,
int flags);
@@ -228,17 +227,25 @@ HgfsInitSuperInfo(HgfsMountInfo *mountInfo) // IN: Passed down from the user
* or gid given to us by the server.
*/
si->uidSet = mountInfo->uidSet;
+ si->uid = current_uid();
if (si->uidSet) {
- si->uid = mountInfo->uid;
- } else {
- si->uid = current_uid();
+ kuid_t mntUid = make_kuid(current_user_ns(), mountInfo->uid);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+ if (uid_valid(mntUid))
+#endif
+ si->uid = mntUid;
}
+
si->gidSet = mountInfo->gidSet;
+ si->gid = current_gid();
if (si->gidSet) {
- si->gid = mountInfo->gid;
- } else {
- si->gid = current_gid();
+ kgid_t mntGid = make_kgid(current_user_ns(), mountInfo->gid);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+ if (gid_valid(mntGid))
+#endif
+ si->gid = mntGid;
}
+
si->fmask = mountInfo->fmask;
si->dmask = mountInfo->dmask;
si->ttl = mountInfo->ttl * HZ; // in ticks
@@ -327,103 +334,6 @@ HgfsInitSuperInfo(HgfsMountInfo *mountInfo) // IN: Passed down from the user
/*
- *----------------------------------------------------------------------------
- *
- * HgfsGetRootDentry --
- *
- * Gets the root dentry for a given super block.
- *
- * Results:
- * zero and a valid root dentry on success
- * negative value on failure
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------------
- */
-
-static int
-HgfsGetRootDentry(struct super_block *sb, // IN: Super block object
- struct dentry **rootDentry) // OUT: Root dentry
-{
- int result = -ENOMEM;
- struct inode *rootInode;
- struct dentry *tempRootDentry = NULL;
- struct HgfsAttrInfo rootDentryAttr;
- HgfsInodeInfo *iinfo;
-
- ASSERT(sb);
- ASSERT(rootDentry);
-
- LOG(6, (KERN_DEBUG "VMware hgfs: %s: entered\n", __func__));
-
- rootInode = HgfsGetInode(sb, HGFS_ROOT_INO);
- if (rootInode == NULL) {
- LOG(6, (KERN_DEBUG "VMware hgfs: %s: Could not get the root inode\n",
- __func__));
- goto exit;
- }
-
- /*
- * On an allocation failure in read_super, the inode will have been
- * marked "bad". If it was, we certainly don't want to start playing with
- * the HgfsInodeInfo. So quietly put the inode back and fail.
- */
- if (is_bad_inode(rootInode)) {
- LOG(6, (KERN_DEBUG "VMware hgfs: %s: encountered bad inode\n",
- __func__));
- goto exit;
- }
-
- tempRootDentry = d_make_root(rootInode);
- /*
- * d_make_root() does iput() on failure; if d_make_root() completes
- * successfully then subsequent dput() will do iput() for us, so we
- * should just ignore root inode from now on.
- */
- rootInode = NULL;
-
- if (tempRootDentry == NULL) {
- LOG(4, (KERN_WARNING "VMware hgfs: %s: Could not get "
- "root dentry\n", __func__));
- goto exit;
- }
-
- result = HgfsPrivateGetattr(tempRootDentry, &rootDentryAttr, NULL);
- if (result) {
- LOG(4, (KERN_WARNING "VMware hgfs: HgfsReadSuper: Could not"
- "instantiate the root dentry\n"));
- goto exit;
- }
-
- iinfo = INODE_GET_II_P(tempRootDentry->d_inode);
- iinfo->isFakeInodeNumber = FALSE;
- iinfo->isReferencedInode = TRUE;
-
- if (rootDentryAttr.mask & HGFS_ATTR_VALID_FILEID) {
- iinfo->hostFileId = rootDentryAttr.hostFileId;
- }
-
- HgfsChangeFileAttributes(tempRootDentry->d_inode, &rootDentryAttr);
- HgfsDentryAgeReset(tempRootDentry);
- tempRootDentry->d_op = &HgfsDentryOperations;
-
- *rootDentry = tempRootDentry;
- result = 0;
-
- LOG(6, (KERN_DEBUG "VMware hgfs: %s: finished\n", __func__));
-exit:
- if (result) {
- iput(rootInode);
- dput(tempRootDentry);
- *rootDentry = NULL;
- }
- return result;
-}
-
-
-/*
*-----------------------------------------------------------------------------
*
* HgfsReadSuper --
@@ -503,7 +413,10 @@ HgfsReadSuper(struct super_block *sb, // OUT: Superblock object
sb->s_blocksize_bits = HgfsComputeBlockBits(HGFS_BLOCKSIZE);
sb->s_blocksize = 1 << sb->s_blocksize_bits;
- result = HgfsGetRootDentry(sb, &rootDentry);
+ /*
+ * Create the root dentry and its corresponding inode.
+ */
+ result = HgfsInstantiateRoot(sb, &rootDentry);
if (result) {
LOG(4, (KERN_WARNING "VMware hgfs: HgfsReadSuper: Could not instantiate "
"root dentry\n"));
diff --git a/modules/linux/vmhgfs/fsutil.c b/modules/linux/vmhgfs/fsutil.c
index 2b1bcff..9cc1137 100644
--- a/modules/linux/vmhgfs/fsutil.c
+++ b/modules/linux/vmhgfs/fsutil.c
@@ -53,11 +53,13 @@ static int HgfsUnpackGetattrReply(HgfsReq *req,
HgfsAttrInfo *attr,
char **fileName);
static int HgfsPackGetattrRequest(HgfsReq *req,
- struct dentry *dentry,
+ HgfsOp opUsed,
Bool allowHandleReuse,
- HgfsOp opUsed,
+ struct dentry *dentry,
HgfsAttrInfo *attr);
-
+static int HgfsBuildRootPath(char *buffer,
+ size_t bufferLen,
+ HgfsSuperInfo *si);
/*
* Private function implementations.
*/
@@ -236,13 +238,17 @@ HgfsUnpackGetattrReply(HgfsReq *req, // IN: Reply packet
/*
*----------------------------------------------------------------------
*
- * HgfsPackGetattrRequest --
+ * HgfsPackCommonattr --
*
- * Setup the getattr request, depending on the op version. When possible,
- * we will issue the getattr using an existing open HGFS handle.
+ * This function abstracts the HgfsAttr struct behind HgfsAttrInfo.
+ * Callers can pass one of four replies into it and receive back the
+ * attributes for those replies.
+ *
+ * Callers must populate attr->requestType so that we know whether to
+ * expect a V1 or V2 Attr struct.
*
* Results:
- * Returns zero on success, or negative error on failure.
+ * Zero on success, non-zero otherwise.
*
* Side effects:
* None
@@ -251,22 +257,18 @@ HgfsUnpackGetattrReply(HgfsReq *req, // IN: Reply packet
*/
static int
-HgfsPackGetattrRequest(HgfsReq *req, // IN/OUT: Request buffer
- struct dentry *dentry, // IN: Dentry containing name
- Bool allowHandleReuse, // IN: Can we use a handle?
- HgfsOp opUsed, // IN: Op to be used
- HgfsAttrInfo *attr) // OUT: Attrs to update
+HgfsPackCommonattr(HgfsReq *req, // IN/OUT: request buffer
+ HgfsOp opUsed, // IN: Op to be used
+ Bool allowHandleReuse, // IN: Can we use a handle?
+ struct inode *fileInode, // IN: file inode
+ size_t *reqSize, // OUT: request size
+ size_t *reqBufferSize, // OUT: request buffer size
+ char **fileName, // OUT: pointer to request file name
+ uint32 **fileNameLength, // OUT: pointer to request file name length
+ HgfsAttrInfo *attr) // OUT: Attrs to update
{
- size_t reqBufferSize;
- size_t reqSize;
- int result = 0;
HgfsHandle handle;
- char *fileName = NULL;
- uint32 *fileNameLength = NULL;
-
- ASSERT(attr);
- ASSERT(dentry);
- ASSERT(req);
+ int result = 0;
attr->requestType = opUsed;
@@ -289,24 +291,25 @@ HgfsPackGetattrRequest(HgfsReq *req, // IN/OUT: Request buffer
* by name.
*/
requestV3->hints = 0;
- if (allowHandleReuse && HgfsGetHandle(dentry->d_inode,
+ if (allowHandleReuse && HgfsGetHandle(fileInode,
0,
&handle) == 0) {
requestV3->fileName.flags = HGFS_FILE_NAME_USE_FILE_DESC;
requestV3->fileName.fid = handle;
requestV3->fileName.length = 0;
requestV3->fileName.caseType = HGFS_FILE_NAME_DEFAULT_CASE;
- fileName = NULL;
+ *fileName = NULL;
+ *fileNameLength = NULL;
} else {
- fileName = requestV3->fileName.name;
- fileNameLength = &requestV3->fileName.length;
+ *fileName = requestV3->fileName.name;
+ *fileNameLength = &requestV3->fileName.length;
requestV3->fileName.flags = 0;
requestV3->fileName.fid = HGFS_INVALID_HANDLE;
requestV3->fileName.caseType = HGFS_FILE_NAME_CASE_SENSITIVE;
}
requestV3->reserved = 0;
- reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(requestV3);
- reqBufferSize = HGFS_NAME_BUFFER_SIZET(req->bufferSize, reqSize);
+ *reqSize = HGFS_REQ_PAYLOAD_SIZE_V3(requestV3);
+ *reqBufferSize = HGFS_NAME_BUFFER_SIZET(req->bufferSize, *reqSize);
break;
}
@@ -323,19 +326,20 @@ HgfsPackGetattrRequest(HgfsReq *req, // IN/OUT: Request buffer
* correct regardless. If we don't find a handle, fall back on getattr
* by name.
*/
- if (allowHandleReuse && HgfsGetHandle(dentry->d_inode,
+ if (allowHandleReuse && HgfsGetHandle(fileInode,
0,
&handle) == 0) {
requestV2->hints = HGFS_ATTR_HINT_USE_FILE_DESC;
requestV2->file = handle;
- fileName = NULL;
+ *fileName = NULL;
+ *fileNameLength = NULL;
} else {
requestV2->hints = 0;
- fileName = requestV2->fileName.name;
- fileNameLength = &requestV2->fileName.length;
+ *fileName = requestV2->fileName.name;
+ *fileNameLength = &requestV2->fileName.length;
}
- reqSize = sizeof *requestV2;
- reqBufferSize = HGFS_NAME_BUFFER_SIZE(req->bufferSize, requestV2);
+ *reqSize = sizeof *requestV2;
+ *reqBufferSize = HGFS_NAME_BUFFER_SIZE(req->bufferSize, requestV2);
break;
}
@@ -346,53 +350,186 @@ HgfsPackGetattrRequest(HgfsReq *req, // IN/OUT: Request buffer
requestV1->header.op = opUsed;
requestV1->header.id = req->id;
- fileName = requestV1->fileName.name;
- fileNameLength = &requestV1->fileName.length;
- reqSize = sizeof *requestV1;
- reqBufferSize = HGFS_NAME_BUFFER_SIZE(req->bufferSize, requestV1);
+ *fileName = requestV1->fileName.name;
+ *fileNameLength = &requestV1->fileName.length;
+ *reqSize = sizeof *requestV1;
+ *reqBufferSize = HGFS_NAME_BUFFER_SIZE(req->bufferSize, requestV1);
break;
}
default:
- LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackGetattrRequest: unexpected "
- "OP type encountered\n"));
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: unexpected OP type encountered\n", __func__));
result = -EPROTO;
+ break;
+ }
+
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsPackGetattrRequest --
+ *
+ * Setup the getattr request, depending on the op version. When possible,
+ * we will issue the getattr using an existing open HGFS handle.
+ *
+ * Results:
+ * Returns zero on success, or negative error on failure.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+HgfsPackGetattrRequest(HgfsReq *req, // IN/OUT: Request buffer
+ HgfsOp opUsed, // IN: Op to be used
+ Bool allowHandleReuse, // IN: Can we use a handle?
+ struct dentry *dentry, // IN: Dentry containing name
+ HgfsAttrInfo *attr) // OUT: Attrs to update
+{
+ size_t reqBufferSize;
+ size_t reqSize;
+ char *fileName = NULL;
+ uint32 *fileNameLength = NULL;
+ int result = 0;
+
+ ASSERT(attr);
+ ASSERT(dentry);
+ ASSERT(req);
+
+ result = HgfsPackCommonattr(req,
+ opUsed,
+ allowHandleReuse,
+ dentry->d_inode,
+ &reqSize,
+ &reqBufferSize,
+ &fileName,
+ &fileNameLength,
+ attr);
+ if (0 > result) {
goto out;
}
/* Avoid all this extra work when we're doing a getattr by handle. */
if (fileName != NULL) {
-
/* Build full name to send to server. */
if (HgfsBuildPath(fileName, reqBufferSize,
dentry) < 0) {
- LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackGetattrRequest: build path "
- "failed\n"));
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: build path failed\n", __func__));
+ result = -EINVAL;
+ goto out;
+ }
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: getting attrs for \"%s\"\n",
+ __func__, fileName));
+
+ /* Convert to CP name. */
+ result = CPName_ConvertTo(fileName,
+ reqBufferSize,
+ fileName);
+ if (result < 0) {
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: CP conversion failed\n", __func__));
+ result = -EINVAL;
+ goto out;
+ }
+
+ *fileNameLength = result;
+ }
+
+ req->payloadSize = reqSize + result;
+ result = 0;
+
+out:
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsPackGetattrRootRequest --
+ *
+ * Setup the getattr request for the root of the HGFS file system.
+ *
+ * When possible, we will issue the getattr using an existing open HGFS handle.
+ *
+ * Results:
+ * Returns zero on success, or negative error on failure.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+HgfsPackGetattrRootRequest(HgfsReq *req, // IN/OUT: Request buffer
+ HgfsOp opUsed, // IN: Op to be used
+ struct super_block *sb, // IN: Super block entry
+ HgfsAttrInfo *attr) // OUT: Attrs to update
+{
+ size_t reqBufferSize;
+ size_t reqSize;
+ char *fileName = NULL;
+ uint32 *fileNameLength = NULL;
+ int result = 0;
+
+ ASSERT(attr);
+ ASSERT(sb);
+ ASSERT(req);
+
+ result = HgfsPackCommonattr(req,
+ opUsed,
+ FALSE,
+ NULL,
+ &reqSize,
+ &reqBufferSize,
+ &fileName,
+ &fileNameLength,
+ attr);
+ if (0 > result) {
+ goto out;
+ }
+
+ /* Avoid all this extra work when we're doing a getattr by handle. */
+ if (fileName != NULL) {
+ HgfsSuperInfo *si = HGFS_SB_TO_COMMON(sb);
+
+ /* Build full name to send to server. */
+ if (HgfsBuildRootPath(fileName,
+ reqBufferSize,
+ si) < 0) {
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: build path failed\n", __func__));
result = -EINVAL;
goto out;
}
- LOG(6, (KERN_DEBUG "VMware hgfs: HgfsPackGetattrRequest: getting attrs "
- "for \"%s\"\n", fileName));
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: getting attrs for \"%s\"\n",
+ __func__, fileName));
/* Convert to CP name. */
result = CPName_ConvertTo(fileName,
reqBufferSize,
fileName);
if (result < 0) {
- LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPackGetattrRequest: CP "
- "conversion failed\n"));
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: CP conversion failed\n", __func__));
result = -EINVAL;
goto out;
}
*fileNameLength = result;
}
+
req->payloadSize = reqSize + result;
result = 0;
+
out:
return result;
}
+
/*
* Public function implementations.
*/
@@ -548,7 +685,7 @@ HgfsUnpackCommonAttr(HgfsReq *req, // IN: Reply packet
/*
*----------------------------------------------------------------------
*
- * HgfsChangeFileAttributes --
+ * HgfsCalcBlockSize --
*
* Calculate the number of 512 byte blocks used.
*
@@ -582,6 +719,148 @@ HgfsCalcBlockSize(uint64 tsize)
#endif
+static inline int
+hgfs_timespec_compare(const struct timespec *lhs, const struct timespec *rhs)
+{
+ if (lhs->tv_sec < rhs->tv_sec)
+ return -1;
+ if (lhs->tv_sec > rhs->tv_sec)
+ return 1;
+ return lhs->tv_nsec - rhs->tv_nsec;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsSetInodeUidGid --
+ *
+ * Set the UID and GID of the inode.
+ *
+ * Update an inode's UID and GID to match those of the HgfsAttr returned
+ * by the server.
+ *
+ * Results:
+ * The number of 512 byte blocks for the size.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+HgfsSetInodeUidGid(struct inode *inode, // IN/OUT: Inode
+ HgfsSuperInfo *si, // IN: New attrs
+ HgfsAttrInfo const *attr) // IN: New attrs
+{
+ /*
+ * Use the stored uid and gid if we were given them at mount-time, or if
+ * the server didn't give us a uid or gid.
+ */
+ if (si->uidSet || (attr->mask & HGFS_ATTR_VALID_USERID) == 0) {
+ inode->i_uid = si->uid;
+ } else {
+ kuid_t attrUid = make_kuid(&init_user_ns, attr->userId);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+ if (uid_valid(attrUid)) {
+ inode->i_uid = attrUid;
+ } else {
+ inode->i_uid = si->uid;
+ }
+#else
+ inode->i_uid = attrUid;
+#endif
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: inode uid %u\n",
+ __func__, from_kuid(&init_user_ns, inode->i_uid)));
+ }
+ if (si->gidSet || (attr->mask & HGFS_ATTR_VALID_GROUPID) == 0) {
+ inode->i_gid = si->gid;
+ } else {
+ kgid_t attrGid = make_kgid(&init_user_ns, attr->groupId);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+ if (gid_valid(attrGid)) {
+ inode->i_gid = attrGid;
+ } else {
+ inode->i_gid = si->gid;
+ }
+#else
+ inode->i_gid = attrGid;
+#endif
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: inode gid %u\n",
+ __func__, from_kgid(&init_user_ns, inode->i_gid)));
+ }
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsIsInodeWritable --
+ *
+ * Helper function for verifying if a file is under write access.
+ *
+ * Results:
+ * TRUE if file is writable, FALSE otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+HgfsIsInodeWritable(struct inode *inode) // IN: File we're writing to
+{
+ HgfsInodeInfo *iinfo;
+ struct list_head *cur;
+ Bool isWritable = FALSE;
+
+ iinfo = INODE_GET_II_P(inode);
+ /*
+ * Iterate over the open handles for this inode, and find if there
+ * is one that allows the write mode.
+ * Note, the mode is stored as incremented by one to prevent overload of
+ * the zero value.
+ */
+ spin_lock(&hgfsBigLock);
+ list_for_each(cur, &iinfo->files) {
+ HgfsFileInfo *finfo = list_entry(cur, HgfsFileInfo, list);
+
+ if (0 != (finfo->mode & (HGFS_OPEN_MODE_WRITE_ONLY + 1))) {
+ isWritable = TRUE;
+ break;
+ }
+ }
+ spin_unlock(&hgfsBigLock);
+
+ return isWritable;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * HgfsIsSafeToChange --
+ *
+ * Helper function for verifying if a file inode size and time fields is safe
+ * to update. It is deemed safe only if there is not an open writer to the file.
+ *
+ * Results:
+ * TRUE if safe to change inode, FALSE otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Bool
+HgfsIsSafeToChange(struct inode *inode) // IN: File we're writing to
+{
+ return !HgfsIsInodeWritable(inode);
+}
+
+
/*
*----------------------------------------------------------------------
*
@@ -606,13 +885,34 @@ HgfsChangeFileAttributes(struct inode *inode, // IN/OUT: Inode
HgfsAttrInfo const *attr) // IN: New attrs
{
HgfsSuperInfo *si;
+ HgfsInodeInfo *iinfo;
Bool needInvalidate = FALSE;
+ Bool isSafeToChange;
ASSERT(inode);
ASSERT(inode->i_sb);
ASSERT(attr);
si = HGFS_SB_TO_COMMON(inode->i_sb);
+ iinfo = INODE_GET_II_P(inode);
+
+ /*
+ * We do not want to update the file size from server or invalidate the inode
+ * for inodes open for write. We need to avoid races with the write page
+ * extending the file. This also will cause the server to possibly update the
+ * server side file's mod time too. For those situations we do not want to blindly
+ * go and invalidate the inode pages thus losing changes in flight and corrupting the
+ * file.
+ * We only need to invalidate the inode pages if the file has truly been modified
+ * on the server side by another server side application, not by our writes.
+ * If there are no writers it is safe to assume that newer mod time means the file
+ * changed on the server side underneath us.
+ */
+ isSafeToChange = HgfsIsSafeToChange(inode);
+
+ spin_lock(&inode->i_lock);
+
+ iinfo = INODE_GET_II_P(inode);
LOG(6, (KERN_DEBUG "VMware hgfs: HgfsChangeFileAttributes: entered\n"));
HgfsSetFileType(inode, attr);
@@ -674,40 +974,27 @@ HgfsChangeFileAttributes(struct inode *inode, // IN/OUT: Inode
*/
set_nlink(inode, 1);
- /*
- * Use the stored uid and gid if we were given them at mount-time, or if
- * the server didn't give us a uid or gid.
- */
- if (si->uidSet || (attr->mask & HGFS_ATTR_VALID_USERID) == 0) {
- inode->i_uid = si->uid;
- } else {
- inode->i_uid = attr->userId;
- }
- if (si->gidSet || (attr->mask & HGFS_ATTR_VALID_GROUPID) == 0) {
- inode->i_gid = si->gid;
- } else {
- inode->i_gid = attr->groupId;
- }
+ HgfsSetInodeUidGid(inode, si, attr);
- inode->i_rdev = 0; /* Device nodes are not supported */
+ inode->i_rdev = 0; /* Device nodes are not supported */
#if !defined VMW_INODE_2618
inode->i_blksize = HGFS_BLOCKSIZE;
#endif
/*
* Invalidate cached pages if we didn't receive the file size, or if it has
- * changed on the server.
+ * changed on the server, and no writes in flight.
*/
if (attr->mask & HGFS_ATTR_VALID_SIZE) {
loff_t oldSize = compat_i_size_read(inode);
- inode->i_blocks = HgfsCalcBlockSize(attr->size);
if (oldSize != attr->size) {
- if (oldSize < attr->size) {
+ if (oldSize < attr->size || (iinfo->numWbPages == 0 && isSafeToChange)) {
needInvalidate = TRUE;
+ LOG(4, (KERN_DEBUG "VMware hgfs: HgfsChangeFileAttributes: new file "
+ "size: %"FMT64"u, old file size: %Lu\n", attr->size, oldSize));
+ inode->i_blocks = HgfsCalcBlockSize(attr->size);
+ compat_i_size_write(inode, attr->size);
}
- LOG(4, (KERN_DEBUG "VMware hgfs: HgfsChangeFileAttributes: new file "
- "size: %"FMT64"u, old file size: %Lu\n", attr->size, oldSize));
- compat_i_size_write(inode, attr->size);
}
} else {
LOG(4, (KERN_DEBUG "VMware hgfs: HgfsChangeFileAttributes: did not "
@@ -722,15 +1009,19 @@ HgfsChangeFileAttributes(struct inode *inode, // IN/OUT: Inode
/*
* Invalidate cached pages if we didn't receive the modification time, or if
- * it has changed on the server.
+ * it has changed on the server and we don't have writes in flight and any open
+ * open writers.
*/
if (attr->mask & HGFS_ATTR_VALID_WRITE_TIME) {
HGFS_DECLARE_TIME(newTime);
HGFS_SET_TIME(newTime, attr->writeTime);
- if (!HGFS_EQUAL_TIME(newTime, inode->i_mtime)) {
+ if (hgfs_timespec_compare(&newTime, &inode->i_mtime) > 0 &&
+ iinfo->numWbPages == 0 &&
+ isSafeToChange) {
LOG(4, (KERN_DEBUG "VMware hgfs: HgfsChangeFileAttributes: new mod "
"time: %ld:%lu, old mod time: %ld:%lu\n",
HGFS_PRINT_TIME(newTime), HGFS_PRINT_TIME(inode->i_mtime)));
+ needInvalidate = TRUE;
}
HGFS_SET_TIME(inode->i_mtime, attr->writeTime);
} else {
@@ -751,6 +1042,8 @@ HgfsChangeFileAttributes(struct inode *inode, // IN/OUT: Inode
HGFS_SET_TIME(inode->i_ctime, HGFS_GET_CURRENT_TIME());
}
+ spin_unlock(&inode->i_lock);
+
/*
* Compare old size and write time with new size and write time. If there's
* a difference (or if we didn't get a new size or write time), the file
@@ -768,17 +1061,14 @@ HgfsChangeFileAttributes(struct inode *inode, // IN/OUT: Inode
/*
*----------------------------------------------------------------------
*
- * HgfsPrivateGetattr --
- *
- * Internal getattr routine. Send a getattr request to the server
- * for the indicated remote name, and if it succeeds copy the
- * results of the getattr into the provided HgfsAttrInfo.
+ * HgfsCanRetryGetattrRequest --
*
- * fileName (if supplied) will be set to a newly allocated string
- * if the file is a symlink; it's the caller's duty to free it.
+ * Checks the getattr request version and downgrades the global getattr
+ * version if we can.
*
* Results:
- * Returns zero on success, or a negative error on failure.
+ * Returns TRUE on success and downgrades the global getattr protocol version,
+ * or FALSE if no retry is possible.
*
* Side effects:
* None
@@ -786,42 +1076,60 @@ HgfsChangeFileAttributes(struct inode *inode, // IN/OUT: Inode
*----------------------------------------------------------------------
*/
-int
-HgfsPrivateGetattr(struct dentry *dentry, // IN: Dentry containing name
- HgfsAttrInfo *attr, // OUT: Attr to copy into
- char **fileName) // OUT: pointer to allocated file name
+static Bool
+HgfsCanRetryGetattrRequest(HgfsOp getattrOp) // IN: getattrOp version used
{
- HgfsReq *req;
- HgfsStatus replyStatus;
- HgfsOp opUsed;
- int result = 0;
- Bool allowHandleReuse = TRUE;
+ Bool canRetry = FALSE;
+
+ /* Retry with older version(s). Set globally. */
+ if (getattrOp == HGFS_OP_GETATTR_V3) {
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: Version 3 "
+ "not supported. Falling back to version 2.\n", __func__));
+ hgfsVersionGetattr = HGFS_OP_GETATTR_V2;
+ canRetry = TRUE;
+ } else if (getattrOp == HGFS_OP_GETATTR_V2) {
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: Version 2 "
+ "not supported. Falling back to version 1.\n", __func__));
+ hgfsVersionGetattr = HGFS_OP_GETATTR;
+ canRetry = TRUE;
+ }
+ return canRetry;
+}
- ASSERT(dentry);
- ASSERT(dentry->d_sb);
- ASSERT(attr);
- req = HgfsGetNewRequest();
- if (!req) {
- LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPrivateGetattr: out of memory "
- "while getting new request\n"));
- result = -ENOMEM;
- goto out;
- }
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsSendGetattrRequest --
+ *
+ * Send the getattr request and handle the reply.
+ *
+ * Results:
+ * Returns zero on success, or a negative error on failure.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
- retry:
+int
+HgfsSendGetattrRequest(HgfsReq *req, // IN: getattr request
+ Bool *doRetry, // OUT: Retry getattr request
+ Bool *allowHandleReuse, // IN/OUT: handle reuse
+ HgfsAttrInfo *attr, // OUT: Attr to copy into
+ char **fileName) // OUT: pointer to allocated file name
+{
+ int result;
- opUsed = hgfsVersionGetattr;
- result = HgfsPackGetattrRequest(req, dentry, allowHandleReuse, opUsed, attr);
- if (result != 0) {
- LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPrivateGetattr: no attrs\n"));
- goto out;
- }
+ *doRetry = FALSE;
result = HgfsSendRequest(req);
if (result == 0) {
- replyStatus = HgfsReplyStatus(req);
+ HgfsStatus replyStatus = HgfsReplyStatus(req);
+
result = HgfsStatusConvertToLinux(replyStatus);
+
LOG(6, (KERN_DEBUG "VMware hgfs: %s: reply status %d -> %d\n",
__func__, replyStatus, result));
@@ -843,7 +1151,7 @@ HgfsPrivateGetattr(struct dentry *dentry, // IN: Dentry containing name
* and it doesn't display any valid shares too. So as a workaround, we
* remap EIO to success and create minimal fake attributes.
*/
- LOG(1, (KERN_DEBUG "Hgfs:Server returned EIO on unknown file\n"));
+ LOG(1, (KERN_DEBUG "Hgfs: %s: Server returned EIO on unknown file\n", __func__));
/* Create fake attributes */
attr->mask = HGFS_ATTR_VALID_TYPE | HGFS_ATTR_VALID_SIZE;
attr->type = HGFS_FILE_TYPE_DIRECTORY;
@@ -860,9 +1168,9 @@ HgfsPrivateGetattr(struct dentry *dentry, // IN: Dentry containing name
* "goto retry" would cause an infinite loop. Instead, let's retry
* with a getattr by name.
*/
- if (allowHandleReuse) {
- allowHandleReuse = FALSE;
- goto retry;
+ if (*allowHandleReuse) {
+ *allowHandleReuse = FALSE;
+ *doRetry = TRUE;
}
/*
@@ -874,30 +1182,143 @@ HgfsPrivateGetattr(struct dentry *dentry, // IN: Dentry containing name
case -EPROTO:
/* Retry with older version(s). Set globally. */
- if (attr->requestType == HGFS_OP_GETATTR_V3) {
- LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPrivateGetattr: Version 3 "
- "not supported. Falling back to version 2.\n"));
- hgfsVersionGetattr = HGFS_OP_GETATTR_V2;
- goto retry;
- } else if (attr->requestType == HGFS_OP_GETATTR_V2) {
- LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPrivateGetattr: Version 2 "
- "not supported. Falling back to version 1.\n"));
- hgfsVersionGetattr = HGFS_OP_GETATTR;
- goto retry;
+ if (HgfsCanRetryGetattrRequest(attr->requestType)) {
+ *doRetry = TRUE;
}
+ break;
- /* Fallthrough. */
default:
break;
}
} else if (result == -EIO) {
- LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPrivateGetattr: timed out\n"));
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: timed out\n", __func__));
} else if (result == -EPROTO) {
- LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPrivateGetattr: server "
- "returned error: %d\n", result));
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: protocol error: %d\n",
+ __func__, result));
} else {
- LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPrivateGetattr: unknown error: "
- "%d\n", result));
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: unknown error: %d\n",
+ __func__, result));
+ }
+
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsPrivateGetattrRoot --
+ *
+ * The getattr for the root. Send a getattr request to the server
+ * for the indicated remote name, and if it succeeds copy the
+ * results of the getattr into the provided HgfsAttrInfo.
+ *
+ * fileName (of the root) will be set to a newly allocated string.
+ *
+ * Results:
+ * Returns zero on success, or a negative error on failure.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+HgfsPrivateGetattrRoot(struct super_block *sb, // IN: Super block object
+ HgfsAttrInfo *attr) // OUT: Attr to copy into
+{
+ HgfsReq *req;
+ HgfsOp opUsed;
+ int result = 0;
+ Bool doRetry;
+ Bool allowHandleReuse = FALSE;
+
+ ASSERT(sb);
+ ASSERT(attr);
+
+ req = HgfsGetNewRequest();
+ if (!req) {
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: out of memory "
+ "while getting new request\n", __func__));
+ result = -ENOMEM;
+ goto out;
+ }
+
+retry:
+ opUsed = hgfsVersionGetattr;
+ result = HgfsPackGetattrRootRequest(req, opUsed, sb, attr);
+ if (result != 0) {
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: no attrs\n", __func__));
+ goto out;
+ }
+
+ result = HgfsSendGetattrRequest(req, &doRetry, &allowHandleReuse, attr, NULL);
+ if (0 != result && doRetry) {
+ goto retry;
+ }
+
+out:
+ HgfsFreeRequest(req);
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsPrivateGetattr --
+ *
+ * Internal getattr routine. Send a getattr request to the server
+ * for the indicated remote name, and if it succeeds copy the
+ * results of the getattr into the provided HgfsAttrInfo.
+ *
+ * fileName (if supplied) will be set to a newly allocated string
+ * if the file is a symlink; it's the caller's duty to free it.
+ *
+ * Results:
+ * Returns zero on success, or a negative error on failure.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+HgfsPrivateGetattr(struct dentry *dentry, // IN: Dentry containing name
+ HgfsAttrInfo *attr, // OUT: Attr to copy into
+ char **fileName) // OUT: pointer to allocated file name
+{
+ HgfsReq *req;
+ HgfsOp opUsed;
+ int result = 0;
+ Bool doRetry;
+ Bool allowHandleReuse = TRUE;
+
+ ASSERT(dentry);
+ ASSERT(dentry->d_sb);
+ ASSERT(attr);
+
+ req = HgfsGetNewRequest();
+ if (!req) {
+ LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPrivateGetattr: out of memory "
+ "while getting new request\n"));
+ result = -ENOMEM;
+ goto out;
+ }
+
+retry:
+ opUsed = hgfsVersionGetattr;
+ result = HgfsPackGetattrRequest(req, opUsed, allowHandleReuse, dentry, attr);
+ if (result != 0) {
+ LOG(4, (KERN_DEBUG "VMware hgfs: HgfsPrivateGetattr: no attrs\n"));
+ goto out;
+ }
+
+ result = HgfsSendGetattrRequest(req, &doRetry, &allowHandleReuse, attr, fileName);
+ if (0 != result && doRetry) {
+ goto retry;
}
out:
@@ -1053,6 +1474,108 @@ HgfsIget(struct super_block *sb, // IN: Superblock of this fs
/*
*-----------------------------------------------------------------------------
*
+ * HgfsInstantiateRoot --
+ *
+ * Gets the root dentry for a given super block.
+ *
+ * Results:
+ * zero and a valid root dentry on success
+ * negative value on failure
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+HgfsInstantiateRoot(struct super_block *sb, // IN: Super block object
+ struct dentry **rootDentry) // OUT: Root dentry
+{
+ int result = -ENOMEM;
+ struct inode *rootInode;
+ struct dentry *tempRootDentry = NULL;
+ struct HgfsAttrInfo rootDentryAttr;
+ HgfsInodeInfo *iinfo;
+
+ ASSERT(sb);
+ ASSERT(rootDentry);
+
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: entered\n", __func__));
+
+ rootInode = HgfsGetInode(sb, HGFS_ROOT_INO);
+ if (rootInode == NULL) {
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: Could not get the root inode\n",
+ __func__));
+ goto exit;
+ }
+
+ /*
+ * On an allocation failure in read_super, the inode will have been
+ * marked "bad". If it was, we certainly don't want to start playing with
+ * the HgfsInodeInfo. So quietly put the inode back and fail.
+ */
+ if (is_bad_inode(rootInode)) {
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: encountered bad inode\n",
+ __func__));
+ goto exit;
+ }
+
+ LOG(8, (KERN_DEBUG "VMware hgfs: %s: retrieve root attrs\n", __func__));
+ result = HgfsPrivateGetattrRoot(sb, &rootDentryAttr);
+ if (result) {
+ LOG(4, (KERN_WARNING "VMware hgfs: %s: Could not the root attrs\n", __func__));
+ goto exit;
+ }
+
+ iinfo = INODE_GET_II_P(rootInode);
+ iinfo->isFakeInodeNumber = FALSE;
+ iinfo->isReferencedInode = TRUE;
+
+ if (rootDentryAttr.mask & HGFS_ATTR_VALID_FILEID) {
+ iinfo->hostFileId = rootDentryAttr.hostFileId;
+ }
+
+ HgfsChangeFileAttributes(rootInode, &rootDentryAttr);
+
+ /*
+ * Now the initialization of the inode is complete we can create
+ * the root dentry which has flags initialized from the inode itself.
+ */
+ tempRootDentry = d_make_root(rootInode);
+ /*
+ * d_make_root() does iput() on failure; if d_make_root() completes
+ * successfully then subsequent dput() will do iput() for us, so we
+ * should just ignore root inode from now on.
+ */
+ rootInode = NULL;
+
+ if (tempRootDentry == NULL) {
+ LOG(4, (KERN_WARNING "VMware hgfs: %s: Could not get "
+ "root dentry\n", __func__));
+ goto exit;
+ }
+
+ HgfsDentryAgeReset(tempRootDentry);
+ tempRootDentry->d_op = &HgfsDentryOperations;
+
+ *rootDentry = tempRootDentry;
+ result = 0;
+
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: finished\n", __func__));
+exit:
+ if (result) {
+ iput(rootInode);
+ dput(tempRootDentry);
+ *rootDentry = NULL;
+ }
+ return result;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
* HgfsInstantiate --
*
* Tie a dentry to a looked up or created inode. Callers may choose to
@@ -1117,6 +1640,45 @@ HgfsInstantiate(struct dentry *dentry, // IN: Dentry to use
/*
*-----------------------------------------------------------------------------
*
+ * HgfsBuildRootPath --
+ *
+ * Constructs the root path given the super info.
+ *
+ * Results:
+ * If non-negative, the length of the buffer written.
+ * Otherwise, an error code.
+ *
+ * Side effects:
+ * None
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+HgfsBuildRootPath(char *buffer, // IN/OUT: Buffer to write into
+ size_t bufferLen, // IN: Size of buffer
+ HgfsSuperInfo *si) // IN: First dentry to walk
+{
+ size_t shortestNameLength;
+ /*
+ * Buffer must hold at least the share name (which is already prefixed with
+ * a forward slash), and nul.
+ */
+ shortestNameLength = si->shareNameLen + 1;
+ if (bufferLen < shortestNameLength) {
+ return -ENAMETOOLONG;
+ }
+ memcpy(buffer, si->shareName, shortestNameLength);
+
+ /* Short-circuit if we're at the root already. */
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: root path \"%s\"\n", __func__, buffer));
+ return shortestNameLength;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
* HgfsBuildPath --
*
* Constructs the full path given a dentry by walking the dentry and its
@@ -1138,7 +1700,7 @@ HgfsBuildPath(char *buffer, // IN/OUT: Buffer to write into
size_t bufferLen, // IN: Size of buffer
struct dentry *dentry) // IN: First dentry to walk
{
- int retval = 0;
+ int retval;
size_t shortestNameLength;
HgfsSuperInfo *si;
@@ -1148,26 +1710,23 @@ HgfsBuildPath(char *buffer, // IN/OUT: Buffer to write into
si = HGFS_SB_TO_COMMON(dentry->d_sb);
- /*
- * Buffer must hold at least the share name (which is already prefixed with
- * a forward slash), and nul.
- */
- shortestNameLength = si->shareNameLen + 1;
- if (bufferLen < shortestNameLength) {
- return -ENAMETOOLONG;
+ retval = HgfsBuildRootPath(buffer, bufferLen, si);
+ if (0 > retval) {
+ return retval;
}
- memcpy(buffer, si->shareName, shortestNameLength);
/* Short-circuit if we're at the root already. */
if (IS_ROOT(dentry)) {
LOG(4, (KERN_DEBUG "VMware hgfs: HgfsBuildPath: Sending root \"%s\"\n",
buffer));
- return shortestNameLength;
+ return retval;
}
/* Skip the share name, but overwrite our previous nul. */
+ shortestNameLength = retval;
buffer += shortestNameLength - 1;
bufferLen -= shortestNameLength - 1;
+ retval = 0;
/*
* Build the path string walking the tree backward from end to ROOT
@@ -1184,8 +1743,8 @@ HgfsBuildPath(char *buffer, // IN/OUT: Buffer to write into
if (bufferLen < 0) {
compat_unlock_dentry(dentry);
dput(dentry);
- LOG(4, (KERN_DEBUG "VMware hgfs: HgfsBuildPath: Ran out of space "
- "while writing dentry name\n"));
+ LOG(4, (KERN_DEBUG "VMware hgfs: HgfsBuildPath: Ran out of space "
+ "while writing dentry name\n"));
return -ENAMETOOLONG;
}
buffer[bufferLen] = '/';
@@ -1259,7 +1818,7 @@ HgfsDentryAgeReset(struct dentry *dentry) // IN: Dentry whose age to reset
/*
*-----------------------------------------------------------------------------
*
- * HgfsDentryAgeReset --
+ * HgfsDentryAgeForce --
*
* Set the dentry's time to 0. This makes the dentry's age "too old" and
* forces subsequent HgfsRevalidates to go to the server for attributes.
@@ -1352,95 +1911,6 @@ HgfsGetOpenMode(uint32 flags) // IN: Open flags
/*
- *----------------------------------------------------------------------
- *
- * HgfsGetOpenFlags --
- *
- * Based on the flags requested by the process making the open()
- * syscall, determine which flags to send to the server to open the
- * file.
- *
- * Results:
- * Returns the correct HgfsOpenFlags enumeration to send to the
- * server, or -1 on failure.
- *
- * Side effects:
- * None
- *
- *----------------------------------------------------------------------
- */
-
-int
-HgfsGetOpenFlags(uint32 flags) // IN: Open flags
-{
- uint32 mask = O_CREAT | O_TRUNC | O_EXCL;
- int result = -1;
-
- LOG(6, (KERN_DEBUG "VMware hgfs: %s: (%u) entered\n", __func__, flags));
-
- /*
- * Mask the flags to only look at O_CREAT, O_EXCL, and O_TRUNC.
- */
-
- flags &= mask;
-
- /* O_EXCL has no meaning if O_CREAT is not set. */
- if (!(flags & O_CREAT)) {
- flags &= ~O_EXCL;
- }
-
- /* Pick the right HgfsOpenFlags. */
- switch (flags) {
-
- case 0:
- /* Regular open; fails if file nonexistant. */
- result = HGFS_OPEN;
- break;
-
- case O_CREAT:
- /* Create file; if it exists already just open it. */
- result = HGFS_OPEN_CREATE;
- break;
-
- case O_TRUNC:
- /* Truncate existing file; fails if nonexistant. */
- result = HGFS_OPEN_EMPTY;
- break;
-
- case (O_CREAT | O_EXCL):
- /* Create file; fail if it exists already. */
- result = HGFS_OPEN_CREATE_SAFE;
- break;
-
- case (O_CREAT | O_TRUNC):
- /* Create file; if it exists already, truncate it. */
- result = HGFS_OPEN_CREATE_EMPTY;
- break;
-
- default:
- /*
- * This can only happen if all three flags are set, which
- * conceptually makes no sense because O_EXCL and O_TRUNC are
- * mutually exclusive if O_CREAT is set.
- *
- * However, the open(2) man page doesn't say you can't set all
- * three flags, and certain apps (*cough* Nautilus *cough*) do
- * so. To be friendly to those apps, we just silenty drop the
- * O_TRUNC flag on the assumption that it's safer to honor
- * O_EXCL.
- */
- LOG(4, (KERN_DEBUG "VMware hgfs: HgfsGetOpenFlags: invalid open "
- "flags %o. Ignoring the O_TRUNC flag.\n", flags));
- result = HGFS_OPEN_CREATE_SAFE;
- break;
- }
-
- LOG(6, (KERN_DEBUG "VMware hgfs: %s: return %d\n", __func__, result));
- return result;
-}
-
-
-/*
*-----------------------------------------------------------------------------
*
* HgfsCreateFileInfo --
@@ -1499,6 +1969,7 @@ HgfsCreateFileInfo(struct file *file, // IN: File pointer to attach to
/* So that readdir() reissues open request */
fileInfo->isStale = TRUE;
+ fileInfo->direntPos = 0;
/*
* I don't think we need any VFS locks since we're only touching the HGFS
@@ -1747,8 +2218,8 @@ HgfsStatusConvertToLinux(HgfsStatus hgfsStatus) // IN: Status code to convert
void
HgfsSetUidGid(struct inode *parent, // IN: parent inode
struct dentry *dentry, // IN: dentry of file to update
- uid_t uid, // IN: uid to set
- gid_t gid) // IN: gid to set
+ kuid_t uid, // IN: uid to set
+ kgid_t gid) // IN: gid to set
{
struct iattr setUidGid;
@@ -1851,5 +2322,7 @@ HgfsDoReadInode(struct inode *inode) // IN: Inode to initialize
iinfo->isReferencedInode = FALSE;
iinfo->isFakeInodeNumber = FALSE;
iinfo->createdAndUnopened = FALSE;
+ iinfo->numWbPages = 0;
+ INIT_LIST_HEAD(&iinfo->listWbPages);
}
diff --git a/modules/linux/vmhgfs/fsutil.h b/modules/linux/vmhgfs/fsutil.h
index f332fb6..e9f1650 100644
--- a/modules/linux/vmhgfs/fsutil.h
+++ b/modules/linux/vmhgfs/fsutil.h
@@ -32,6 +32,7 @@
#include <linux/signal.h>
#include "compat_fs.h"
+#include "module.h"
#include "inode.h"
#include "request.h"
#include "vm_basic_types.h"
@@ -73,6 +74,8 @@ int HgfsPrivateGetattr(struct dentry *dentry,
struct inode *HgfsIget(struct super_block *sb,
ino_t ino,
HgfsAttrInfo const *attr);
+int HgfsInstantiateRoot(struct super_block *sb,
+ struct dentry **rootDentry);
int HgfsInstantiate(struct dentry *dentry,
ino_t ino,
HgfsAttrInfo const *attr);
@@ -82,7 +85,6 @@ int HgfsBuildPath(char *buffer,
void HgfsDentryAgeReset(struct dentry *dentry);
void HgfsDentryAgeForce(struct dentry *dentry);
int HgfsGetOpenMode(uint32 flags);
-int HgfsGetOpenFlags(uint32 flags);
int HgfsCreateFileInfo(struct file *file,
HgfsHandle handle);
void HgfsReleaseFileInfo(struct file *file);
@@ -92,8 +94,8 @@ int HgfsGetHandle(struct inode *inode,
int HgfsStatusConvertToLinux(HgfsStatus hgfsStatus);
void HgfsSetUidGid(struct inode *parent,
struct dentry *dentry,
- uid_t uid,
- gid_t gid);
+ kuid_t uid,
+ kgid_t gid);
struct inode *HgfsGetInode(struct super_block *sb, ino_t ino);
void HgfsDoReadInode(struct inode *inode);
diff --git a/modules/linux/vmhgfs/inode.c b/modules/linux/vmhgfs/inode.c
index 2999b94..93e28bf 100644
--- a/modules/linux/vmhgfs/inode.c
+++ b/modules/linux/vmhgfs/inode.c
@@ -33,6 +33,7 @@
#include <linux/highmem.h>
#include "compat_cred.h"
+#include "compat_dcache.h"
#include "compat_fs.h"
#include "compat_kernel.h"
#include "compat_mm.h"
@@ -429,6 +430,8 @@ HgfsPackSetattrRequest(struct iattr *iattr, // IN: Inode attrs to update from
size_t reqBufferSize;
size_t reqSize;
int result = 0;
+ uid_t attrUid = -1;
+ gid_t attrGid = -1;
ASSERT(iattr);
ASSERT(dentry);
@@ -437,6 +440,14 @@ HgfsPackSetattrRequest(struct iattr *iattr, // IN: Inode attrs to update from
valid = iattr->ia_valid;
+ if (valid & ATTR_UID) {
+ attrUid = from_kuid(&init_user_ns, iattr->ia_uid);
+ }
+
+ if (valid & ATTR_GID) {
+ attrGid = from_kgid(&init_user_ns, iattr->ia_gid);
+ }
+
switch (opUsed) {
case HGFS_OP_SETATTR_V3: {
HgfsRequest *requestHeader;
@@ -513,13 +524,13 @@ HgfsPackSetattrRequest(struct iattr *iattr, // IN: Inode attrs to update from
if (valid & ATTR_UID) {
attrV2->mask |= HGFS_ATTR_VALID_USERID;
- attrV2->userId = iattr->ia_uid;
+ attrV2->userId = attrUid;
*changed = TRUE;
}
if (valid & ATTR_GID) {
attrV2->mask |= HGFS_ATTR_VALID_GROUPID;
- attrV2->groupId = iattr->ia_gid;
+ attrV2->groupId = attrGid;
*changed = TRUE;
}
@@ -616,13 +627,13 @@ HgfsPackSetattrRequest(struct iattr *iattr, // IN: Inode attrs to update from
if (valid & ATTR_UID) {
attrV2->mask |= HGFS_ATTR_VALID_USERID;
- attrV2->userId = iattr->ia_uid;
+ attrV2->userId = attrUid;
*changed = TRUE;
}
if (valid & ATTR_GID) {
attrV2->mask |= HGFS_ATTR_VALID_GROUPID;
- attrV2->groupId = iattr->ia_gid;
+ attrV2->groupId = attrGid;
*changed = TRUE;
}
@@ -1890,7 +1901,7 @@ HgfsPermission(struct inode *inode,
#endif
&inode->i_dentry,
d_alias) {
- int dcount = dentry->d_count;
+ int dcount = compat_d_count(dentry);
if (dcount) {
LOG(4, ("Found %s %d \n", dentry->d_name.name, dcount));
return HgfsAccessInt(dentry, mask & (MAY_READ | MAY_WRITE | MAY_EXEC));
@@ -1943,11 +1954,7 @@ HgfsPermission(struct inode *inode,
list_for_each(pos, &inode->i_dentry) {
int dcount;
struct dentry *dentry = list_entry(pos, struct dentry, d_alias);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
- dcount = atomic_read(&dentry->d_count);
-#else
- dcount = dentry->d_count;
-#endif
+ dcount = compat_d_count(dentry);
if (dcount) {
LOG(4, ("Found %s %d \n", (dentry)->d_name.name, dcount));
return HgfsAccessInt(dentry, mask & (MAY_READ | MAY_WRITE | MAY_EXEC));
diff --git a/modules/linux/vmhgfs/link.c b/modules/linux/vmhgfs/link.c
index 65d8a23..3a07f40 100644
--- a/modules/linux/vmhgfs/link.c
+++ b/modules/linux/vmhgfs/link.c
@@ -183,11 +183,19 @@ HgfsReadlink(struct dentry *dentry, // IN: Dentry containing link
"on something that wasn't a symlink\n"));
error = -EINVAL;
} else {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: calling readlink_copy %s\n",
+ __func__, fileName));
+ error = readlink_copy(buffer, buflen, fileName);
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: readlink_copy %s ret %d\n",
+ __func__, fileName, error));
+#else
LOG(6, (KERN_DEBUG "VMware hgfs: %s: calling vfs_readlink %s\n",
__func__, fileName));
error = vfs_readlink(dentry, buffer, buflen, fileName);
- LOG(6, (KERN_DEBUG "VMware hgfs: %s: vfs_readlink %s ret %dn",
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: vfs_readlink %s ret %d\n",
__func__, fileName, error));
+#endif
}
kfree(fileName);
}
diff --git a/modules/linux/vmhgfs/module.h b/modules/linux/vmhgfs/module.h
index 911ba8b..76ebb14 100644
--- a/modules/linux/vmhgfs/module.h
+++ b/modules/linux/vmhgfs/module.h
@@ -74,6 +74,17 @@ extern int LOGLEVEL_THRESHOLD;
* Macros for accessing members that are private to this code in
* sb/inode/file structs.
*/
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
+typedef uid_t kuid_t;
+typedef gid_t kgid_t;
+#define from_kuid(_ns, _kuid) (_kuid)
+#define from_kgid(_ns, _kgid) (_kgid)
+#define make_kuid(_ns, _uid) (_uid)
+#define make_kgid(_ns, _gid) (_gid)
+#endif
+
+
#define HGFS_SET_SB_TO_COMMON(sb, common) do { (sb)->s_fs_info = (common); } while (0)
#define HGFS_SB_TO_COMMON(sb) ((HgfsSuperInfo *)(sb)->s_fs_info)
@@ -110,9 +121,9 @@ extern int LOGLEVEL_THRESHOLD;
/* Data kept in each superblock in sb->u. */
typedef struct HgfsSuperInfo {
- uid_t uid; /* UID of user who mounted this fs. */
+ kuid_t uid; /* UID of user who mounted this fs. */
+ kgid_t gid; /* GID of user who mounted this fs. */
Bool uidSet; /* Was the UID specified at mount-time? */
- gid_t gid; /* GID of user who mounted this fs. */
Bool gidSet; /* Was the GID specified at mount-time? */
mode_t fmask; /* File permission mask. */
mode_t dmask; /* Directory permission mask. */
@@ -137,6 +148,13 @@ typedef struct HgfsInodeInfo {
/* Is this a fake inode created in HgfsCreate that has yet to be opened? */
Bool createdAndUnopened;
+ /*
+ * The number of write back pages to the file which is tracked so any
+ * concurrent file validations such as reads will not invalidate the cache.
+ */
+ unsigned long numWbPages;
+ struct list_head listWbPages;
+
/* Is this inode referenced by HGFS? (needed by HgfsInodeLookup()) */
Bool isReferencedInode;
@@ -167,6 +185,9 @@ typedef struct HgfsFileInfo {
*/
Bool isStale;
+ /* Directory read position for tracking. */
+ loff_t direntPos;
+
} HgfsFileInfo;
diff --git a/modules/linux/vmhgfs/page.c b/modules/linux/vmhgfs/page.c
index 387bc02..018aa8b 100644
--- a/modules/linux/vmhgfs/page.c
+++ b/modules/linux/vmhgfs/page.c
@@ -74,6 +74,8 @@ static int HgfsDoWriteEnd(struct file *file,
unsigned pageTo,
loff_t writeTo,
unsigned copied);
+static void HgfsDoExtendFile(struct inode *inode,
+ loff_t writeTo);
/* HGFS address space operations. */
static int HgfsReadpage(struct file *file,
@@ -130,6 +132,28 @@ struct address_space_operations HgfsAddressSpaceOperations = {
};
+enum {
+ PG_BUSY = 0,
+};
+
+typedef struct HgfsWbPage {
+ struct list_head wb_list; /* Defines state of page: */
+ struct page *wb_page; /* page to read in/write out */
+ pgoff_t wb_index; /* Offset >> PAGE_CACHE_SHIFT */
+ struct kref wb_kref; /* reference count */
+ unsigned long wb_flags;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 13)
+ wait_queue_head_t wb_queue;
+#endif
+} HgfsWbPage;
+
+static void HgfsInodePageWbAdd(struct inode *inode,
+ struct page *page);
+static void HgfsInodePageWbRemove(struct inode *inode,
+ struct page *page);
+static void HgfsWbRequestDestroy(HgfsWbPage *req);
+
+
/*
* Private functions.
*/
@@ -696,14 +720,12 @@ HgfsDoWritepage(HgfsHandle handle, // IN: Handle to use for writing
pageFrom += result;
/* Update the inode's size now rather than waiting for a revalidate. */
- if (curOffset > compat_i_size_read(inode)) {
- compat_i_size_write(inode, curOffset);
- }
+ HgfsDoExtendFile(inode, curOffset);
} while ((result > 0) && (remainingCount > 0));
+ HgfsInodePageWbRemove(inode, page);
+
result = 0;
- LOG(4, (KERN_WARNING "VMware hgfs: %s: end writes at %Lu rem %zu\n",
- __func__, curOffset, remainingCount));
out:
return result;
@@ -1015,6 +1037,40 @@ exit:
/*
*-----------------------------------------------------------------------------
*
+ * HgfsDoExtendFile --
+ *
+ * Helper function for extending a file size.
+ *
+ * This function updates the inode->i_size, under the inode lock.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+HgfsDoExtendFile(struct inode *inode, // IN: File we're writing to
+ loff_t writeTo) // IN: Offset we're written to
+{
+ loff_t currentFileSize;
+
+ spin_lock(&inode->i_lock);
+ currentFileSize = compat_i_size_read(inode);
+
+ if (writeTo > currentFileSize) {
+ compat_i_size_write(inode, writeTo);
+ }
+ spin_unlock(&inode->i_lock);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
* HgfsDoWriteEnd --
*
* Helper function for HgfsWriteEnd.
@@ -1039,18 +1095,11 @@ HgfsDoWriteEnd(struct file *file, // IN: File we're writing to
loff_t writeTo, // IN: File position to write to
unsigned copied) // IN: Number of bytes copied to the page
{
- loff_t currentFileSize;
struct inode *inode;
ASSERT(file);
ASSERT(page);
inode = page->mapping->host;
- currentFileSize = compat_i_size_read(inode);
-
- LOG(6, (KERN_WARNING "VMware hgfs: %s: (%s/%s(%ld), from %u to %u@%lld => %u)\n",
- __func__, file->f_dentry->d_parent->d_name.name,
- file->f_dentry->d_name.name,
- page->mapping->host->i_ino, pageFrom, pageTo, (long long) writeTo, copied));
/*
* Zero any uninitialised parts of the page, and then mark the page
@@ -1060,9 +1109,12 @@ HgfsDoWriteEnd(struct file *file, // IN: File we're writing to
SetPageUptodate(page);
}
- if (writeTo > currentFileSize) {
- compat_i_size_write(inode, writeTo);
- }
+ /*
+ * Track the pages being written.
+ */
+ HgfsInodePageWbAdd(inode, page);
+
+ HgfsDoExtendFile(inode, writeTo);
set_page_dirty(page);
@@ -1177,3 +1229,671 @@ HgfsWriteEnd(struct file *file, // IN: File to write
return ret;
}
#endif
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsWbPageAlloc --
+ *
+ * Allocates a write-back page object.
+ *
+ * Results:
+ * The write-back page object
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static inline HgfsWbPage *
+HgfsWbPageAlloc(void)
+{
+ return kmalloc(sizeof (HgfsWbPage), GFP_KERNEL);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsWbPageAlloc --
+ *
+ * Frees a write-back page object.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+
+static inline void
+HgfsWbPageFree(HgfsWbPage *page) // IN: request of page data to write
+{
+ ASSERT(page);
+ kfree(page);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsWbRequestFree --
+ *
+ * Frees the resources for a write-back page request.
+ * Calls the request destroy and then frees the object memory.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+HgfsWbRequestFree(struct kref *kref) // IN: ref field request of page data to write
+{
+ HgfsWbPage *req = container_of(kref, HgfsWbPage, wb_kref);
+
+ /* Release write back request page and free it. */
+ HgfsWbRequestDestroy(req);
+ HgfsWbPageFree(req);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsWbRequestGet --
+ *
+ * Reference the write-back page request.
+ * Calls the request destroy and then frees the object memory.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+HgfsWbRequestGet(HgfsWbPage *req) // IN: request of page data to write
+{
+ kref_get(&req->wb_kref);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsWbRequestPut --
+ *
+ * Remove a reference the write-back page request.
+ * Calls the request free to tear down the object memory if it was the
+ * final one.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * Destroys the request if last one.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+HgfsWbRequestPut(HgfsWbPage *req) // IN: request of page data to write
+{
+ kref_put(&req->wb_kref, HgfsWbRequestFree);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsWbRequestWaitUninterruptible --
+ *
+ * Sleep function while waiting for requests to complete.
+ *
+ * Results:
+ * Always zero.
+ *
+ * Side effects:
+* None
+ *
+ *----------------------------------------------------------------------
+ */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
+static int
+HgfsWbRequestWaitUninterruptible(void *word) // IN:unused
+{
+ io_schedule();
+ return 0;
+}
+#endif
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsWbRequestWait --
+ *
+ * Wait for a write-back page request to complete.
+ * Interruptible by fatal signals only.
+ * The user is responsible for holding a count on the request.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+
+int
+HgfsWbRequestWait(HgfsWbPage *req) // IN: request of page data to write
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
+ return wait_on_bit(&req->wb_flags,
+ PG_BUSY,
+ HgfsWbRequestWaitUninterruptible,
+ TASK_UNINTERRUPTIBLE);
+#else
+ wait_event(req->wb_queue,
+ !test_bit(PG_BUSY, &req->wb_flags));
+ return 0;
+#endif
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsWbRequestLock --
+ *
+ * Lock the write-back page request.
+ *
+ * Results:
+ * Non-zero if the lock was not already locked
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static inline int
+HgfsWbRequestLock(HgfsWbPage *req) // IN: request of page data to write
+{
+ return !test_and_set_bit(PG_BUSY, &req->wb_flags);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsWbRequestUnlock --
+ *
+ * Unlock the write-back page request.
+ * Wakes up any waiting threads on the lock.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+HgfsWbRequestUnlock(HgfsWbPage *req) // IN: request of page data to write
+{
+ if (!test_bit(PG_BUSY,&req->wb_flags)) {
+ LOG(6, (KERN_WARNING "VMware Hgfs: %s: Invalid unlock attempted\n", __func__));
+ return;
+ }
+ smp_mb__before_clear_bit();
+ clear_bit(PG_BUSY, &req->wb_flags);
+ smp_mb__after_clear_bit();
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 13)
+ wake_up_bit(&req->wb_flags, PG_BUSY);
+#else
+ wake_up(&req->wb_queue);
+#endif
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsWbRequestUnlockAndPut --
+ *
+ * Unlock the write-back page request and removes a reference.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+HgfsWbRequestUnlockAndPut(HgfsWbPage *req) // IN: request of page data to write
+{
+ HgfsWbRequestUnlock(req);
+ HgfsWbRequestPut(req);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsWbRequestListAdd --
+ *
+ * Add the write-back page request into the list.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static inline void
+HgfsWbRequestListAdd(HgfsWbPage *req, // IN: request of page data to write
+ struct list_head *head) // IN: list of requests
+{
+ list_add_tail(&req->wb_list, head);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsWbRequestListRemove --
+ *
+ * Remove the write-back page request from the list.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static inline void
+HgfsWbRequestListRemove(HgfsWbPage *req) // IN: request of page data to write
+{
+ if (!list_empty(&req->wb_list)) {
+ list_del_init(&req->wb_list);
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsWbRequestCreate --
+ *
+ * Create the write-back page request.
+ *
+ * Results:
+ * The new write-back page request.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+HgfsWbPage *
+HgfsWbRequestCreate(struct page *page) // IN: page of data to write
+{
+ HgfsWbPage *wbReq;
+ /* try to allocate the request struct */
+ wbReq = HgfsWbPageAlloc();
+ if (wbReq == NULL) {
+ wbReq = ERR_PTR(-ENOMEM);
+ goto exit;
+ }
+
+ /*
+ * Initialize the request struct. Initially, we assume a
+ * long write-back delay. This will be adjusted in
+ * update_nfs_request below if the region is not locked.
+ */
+ wbReq->wb_flags = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 13)
+ init_waitqueue_head(&wbReq->wb_queue);
+#endif
+ INIT_LIST_HEAD(&wbReq->wb_list);
+ wbReq->wb_page = page;
+ wbReq->wb_index = page->index;
+ page_cache_get(page);
+ kref_init(&wbReq->wb_kref);
+
+exit:
+ LOG(6, (KERN_WARNING "VMware hgfs: %s: (%p, %p)\n",
+ __func__, wbReq, page));
+ return wbReq;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsWbRequestDestroy --
+ *
+ * Destroys by freeing up all resources allocated to the request.
+ * Release page associated with a write-back request after it has completed.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+HgfsWbRequestDestroy(HgfsWbPage *req) // IN: write page request
+{
+ struct page *page = req->wb_page;
+
+ LOG(6, (KERN_WARNING"VMware hgfs: %s: (%p, %p)\n",
+ __func__, req, req->wb_page));
+
+ if (page != NULL) {
+ page_cache_release(page);
+ req->wb_page = NULL;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsInodeFindWbRequest --
+ *
+ * Finds if there is a write-back page request on this inode and returns it.
+ *
+ * Results:
+ * NULL or the write-back request for the page.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static HgfsWbPage *
+HgfsInodeFindWbRequest(struct inode *inode, // IN: inode of file to write to
+ struct page *page) // IN: page of data to write
+{
+ HgfsInodeInfo *iinfo;
+ HgfsWbPage *req = NULL;
+ HgfsWbPage *cur;
+
+ iinfo = INODE_GET_II_P(inode);
+
+ /* Linearly search the write back list for the correct req */
+ list_for_each_entry(cur, &iinfo->listWbPages, wb_list) {
+ if (cur->wb_page == page) {
+ req = cur;
+ break;
+ }
+ }
+
+ if (req != NULL) {
+ HgfsWbRequestGet(req);
+ }
+
+ return req;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsInodeFindExistingWbRequest --
+ *
+ * Finds if there is a write-back page request on this inode and returns
+ * locked.
+ * If the request is busy (locked) then it drops the lock and waits for it
+ * be not locked and searches the list again.
+ *
+ * Results:
+ * NULL or the write-back request for the page.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static HgfsWbPage *
+HgfsInodeFindExistingWbRequest(struct inode *inode, // IN: inode of file to write to
+ struct page *page) // IN: page of data to write
+{
+ HgfsWbPage *req;
+ int error;
+
+ spin_lock(&inode->i_lock);
+
+ for (;;) {
+ req = HgfsInodeFindWbRequest(inode, page);
+ if (req == NULL) {
+ goto out_exit;
+ }
+
+ /*
+ * Try and lock the request if not already locked.
+ * If we find it is already locked, busy, then we drop
+ * the reference and wait to try again. Otherwise,
+ * once newly locked we break out and return to the caller.
+ */
+ if (HgfsWbRequestLock(req)) {
+ break;
+ }
+
+ /* The request was in use, so wait and then retry */
+ spin_unlock(&inode->i_lock);
+ error = HgfsWbRequestWait(req);
+ HgfsWbRequestPut(req);
+ if (error != 0) {
+ goto out_nolock;
+ }
+
+ spin_lock(&inode->i_lock);
+ }
+
+out_exit:
+ spin_unlock(&inode->i_lock);
+ return req;
+
+out_nolock:
+ return ERR_PTR(error);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsInodeAddWbRequest --
+ *
+ * Add a write-back page request to an inode.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+HgfsInodeAddWbRequest(struct inode *inode, // IN: inode of file to write to
+ HgfsWbPage *req) // IN: page write request
+{
+ HgfsInodeInfo *iinfo = INODE_GET_II_P(inode);
+
+ LOG(6, (KERN_WARNING "VMware hgfs: %s: (%p, %p, %lu)\n",
+ __func__, inode, req->wb_page, iinfo->numWbPages));
+
+ /* Lock the request! */
+ HgfsWbRequestLock(req);
+
+ HgfsWbRequestListAdd(req, &iinfo->listWbPages);
+ iinfo->numWbPages++;
+ HgfsWbRequestGet(req);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsInodeAddWbRequest --
+ *
+ * Remove a write-back page request from an inode.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+HgfsInodeRemoveWbRequest(struct inode *inode, // IN: inode of file written to
+ HgfsWbPage *req) // IN: page write request
+{
+ HgfsInodeInfo *iinfo = INODE_GET_II_P(inode);
+
+ LOG(6, (KERN_CRIT "VMware hgfs: %s: (%p, %p, %lu)\n",
+ __func__, inode, req->wb_page, iinfo->numWbPages));
+
+ iinfo->numWbPages--;
+ HgfsWbRequestListRemove(req);
+ HgfsWbRequestPut(req);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsInodeAddWbRequest --
+ *
+ * Add a write-back page request to an inode.
+ * If the page is already exists in the list for this inode nothing is
+ * done, otherwise a new object is created for the page and added to the
+ * inode list.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+HgfsInodePageWbAdd(struct inode *inode, // IN: inode of file to write to
+ struct page *page) // IN: page of data to write
+{
+ HgfsWbPage *req;
+
+ LOG(6, (KERN_CRIT "VMware hgfs: %s: (%p, %p)\n",
+ __func__, inode, page));
+
+ req = HgfsInodeFindExistingWbRequest(inode, page);
+ if (req != NULL) {
+ goto exit;
+ }
+
+ /*
+ * We didn't find an existing write back request for that page so
+ * we create one.
+ */
+ req = HgfsWbRequestCreate(page);
+ if (IS_ERR(req)) {
+ goto exit;
+ }
+
+ spin_lock(&inode->i_lock);
+ /*
+ * Add the new write request for the page into our inode list to track.
+ */
+ HgfsInodeAddWbRequest(inode, req);
+ spin_unlock(&inode->i_lock);
+
+exit:
+ if (!IS_ERR(req)) {
+ HgfsWbRequestUnlockAndPut(req);
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsInodePageWbRemove --
+ *
+ * Remove a write-back page request from an inode.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+HgfsInodePageWbRemove(struct inode *inode, // IN: inode of file written to
+ struct page *page) // IN: page of data written
+{
+ HgfsWbPage *req;
+
+ LOG(6, (KERN_WARNING "VMware hgfs: %s: (%p, %p)\n",
+ __func__, inode, page));
+
+ req = HgfsInodeFindExistingWbRequest(inode, page);
+ if (req == NULL) {
+ goto exit;
+ }
+ spin_lock(&inode->i_lock);
+ /*
+ * Add the new write request for the page into our inode list to track.
+ */
+ HgfsInodeRemoveWbRequest(inode, req);
+ HgfsWbRequestUnlockAndPut(req);
+ spin_unlock(&inode->i_lock);
+
+exit:
+ return;
+}
+
--
2.0.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment