Created
July 10, 2014 21:14
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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