summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVMware, Inc <>2013-09-17 20:36:04 -0700
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2013-09-22 22:26:42 -0700
commit4a527c999422eaba9795b18e89b69fbeefda8583 (patch)
tree79a16e7930cfc2d909ad2d08b85dfbf8e254909c
parent2946894f1783f7a735d2e3d3770853b9b29f95ea (diff)
HGFS: Make Linux client compile on kernels 3.11
HgfsReaddir has been replaced with HgfsIterate for the newer releases of the kernel 3.11 and newer. Signed-off-by: Dmitry Torokhov <dtor@vmware.com>
-rw-r--r--open-vm-tools/modules/linux/vmhgfs/dir.c718
1 files changed, 538 insertions, 180 deletions
diff --git a/open-vm-tools/modules/linux/vmhgfs/dir.c b/open-vm-tools/modules/linux/vmhgfs/dir.c
index 1d0351ae..e4e82b48 100644
--- a/open-vm-tools/modules/linux/vmhgfs/dir.c
+++ b/open-vm-tools/modules/linux/vmhgfs/dir.c
@@ -60,13 +60,27 @@ static int HgfsGetNextDirEntry(HgfsSuperInfo *si,
static int HgfsPackDirOpenRequest(struct file *file,
HgfsOp opUsed,
HgfsReq *req);
+static Bool
+HgfsReaddirFillEntry(filldir_t filldirCb,
+ void *context,
+ char *entryName,
+ uint32 entryNameLength,
+ loff_t entryPos,
+ ino_t entryIno,
+ uint32 entryType);
/* HGFS file operations for directories. */
static int HgfsDirOpen(struct inode *inode,
struct file *file);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+static int HgfsReaddir(struct file *file,
+ struct dir_context *ctx);
+#else
static int HgfsReaddir(struct file *file,
void *dirent,
filldir_t filldir);
+#endif
static int HgfsDirRelease(struct inode *inode,
struct file *file);
static loff_t HgfsDirLlseek(struct file *file,
@@ -79,7 +93,11 @@ struct file_operations HgfsDirFileOperations = {
.owner = THIS_MODULE,
.open = HgfsDirOpen,
.read = generic_read_dir,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ .iterate = HgfsReaddir,
+#else
.readdir = HgfsReaddir,
+#endif
.release = HgfsDirRelease,
};
@@ -794,7 +812,303 @@ HgfsDirOpen(struct inode *inode, // IN: Inode of the dir to open
/*
*----------------------------------------------------------------------
*
- * HgfsReaddir --
+ * HgfsReaddirRefreshEntries --
+ *
+ * refresh the file entries if the handle is stale by reopening.
+ *
+ * Results:
+ * Zero on success, otherwise failure.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+HgfsReaddirRefreshEntries(struct file *file) // IN: File pointer for this open
+{
+ int result = 0;
+
+ /*
+ * rm -rf 6.10+ breaks because it does following:
+ * an 'fd = open()' on a directory, followed by unlinkat()
+ * which removes an entry from the directory it and then
+ * fdopendir(fd). We get a call on open() but not on fdopendir(),
+ * which means that we do not reflect the action of unlinkat(),
+ * and thus rm -rf gets confused and marking entry as unremovable.
+ * Note that this problem exists because hgfsServer reads all
+ * the directory entries at open(). Interested reader may look at
+ * coreutils/src/remove.c file.
+ *
+ * So as a workaround, we ask the server to populate entries on
+ * first readdir() call rather than opendir(). This effect is
+ * achieved by closing and reopening the directory. Grrr!
+ *
+ * XXX We should get rid of this code when/if we remove the above
+ * behavior from hgfsServer.
+ */
+ if (FILE_GET_FI_P(file)->isStale) {
+ result = HgfsPrivateDirReOpen(file);
+ }
+
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: error: stale handle (%s) return %d)\n",
+ __func__, file->f_dentry->d_name.name, result));
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsGetFileInode --
+ *
+ * Get file inode from the hgfs attributes or generate from the super block.
+ *
+ * Results:
+ * The inode entry.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static ino_t
+HgfsGetFileInode(HgfsAttrInfo const *attr, // IN: Attrs to use
+ struct super_block *sb) // IN: Superblock of this fs
+{
+ ino_t inodeEntry;
+
+ ASSERT(attr);
+ ASSERT(sb);
+
+ if (attr->mask & HGFS_ATTR_VALID_FILEID) {
+ inodeEntry = attr->hostFileId;
+ } else {
+ inodeEntry = iunique(sb, HGFS_RESERVED_INO);
+ }
+
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: return %lu\n", __func__, inodeEntry));
+ return inodeEntry;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsGetFileType --
+ *
+ * Get file type according to the hgfs attributes.
+ *
+ * Results:
+ * The file type.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static uint32
+HgfsGetFileType(HgfsAttrInfo const *attr) // IN: Attrs to use
+{
+ uint32 type;
+
+ ASSERT(attr);
+
+ switch (attr->type) {
+ case HGFS_FILE_TYPE_SYMLINK:
+ type = DT_LNK;
+ break;
+
+ case HGFS_FILE_TYPE_REGULAR:
+ type = DT_REG;
+ break;
+
+ case HGFS_FILE_TYPE_DIRECTORY:
+ type = DT_DIR;
+ break;
+
+ default:
+ /*
+ * XXX Should never happen. I'd put NOT_IMPLEMENTED() here
+ * but if the driver ever goes in the host it's probably not
+ * a good idea for an attacker to be able to hang the host
+ * simply by using a bogus file type in a reply. [bac]
+ *
+ * Well it happens! Refer bug 548177 for details. In short,
+ * when the user deletes a share, we hit this code path.
+ *
+ */
+ type = DT_UNKNOWN;
+ break;
+ }
+
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: return %d\n", __func__, type));
+ return type;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsReaddirNextEntry --
+ *
+ * Called whenever a process opens a directory in our filesystem.
+ *
+ * We send a "Search Open" request to the server with the name
+ * stored in this file's inode. If the Open succeeds, we store the
+ * search handle sent by the server in the file struct so it can be
+ * accessed by readdir and close.
+ *
+ * Results:
+ * Returns zero if on success, error on failure.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+HgfsReaddirNextEntry(struct file *file, // IN: file
+ loff_t entryPos, // IN: position
+ Bool dotAndDotDotIgnore, // IN: ignore "." and ".."
+ size_t entryNameBufLen, // IN: name buffer length
+ char *entryName, // OUT: entry name
+ uint32 *entryNameLength, // OUT: max name length
+ ino_t *entryIno, // OUT: inode entry number
+ uint32 *entryType, // OUT: entry type
+ Bool *entryIgnore, // OUT: ignore this entry or not
+ Bool *entryEnd) // OUT: no more entries
+{
+ HgfsSuperInfo *si;
+ HgfsAttrInfo entryAttrs;
+ char *fileName = NULL;
+ int result;
+
+ ASSERT(file->f_dentry->d_inode->i_sb);
+
+ si = HGFS_SB_TO_COMMON(file->f_dentry->d_inode->i_sb);
+ *entryIgnore = FALSE;
+
+ /*
+ * Nonzero result = we failed to get valid reply from server.
+ * Zero result:
+ * - done == TRUE means we hit the end of the directory
+ * - Otherwise, fileName has the name of the next dirent
+ *
+ */
+
+ result = HgfsGetNextDirEntry(si,
+ FILE_GET_FI_P(file)->handle,
+ (uint32)entryPos,
+ &entryAttrs,
+ &fileName,
+ entryEnd);
+ if (result == -ENAMETOOLONG) {
+ /*
+ * Skip dentry if its name is too long (see below).
+ *
+ * XXX: If a bad server sends us bad packets, we can loop here
+ * forever, as I did while testing *grumble*. Maybe we should error
+ * in that case.
+ */
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: error getnextdentry name %d\n",
+ __func__, result));
+ *entryIgnore = TRUE;
+ result = 0;
+ goto exit;
+ } else if (result) {
+ /* Error */
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s: error getnextdentry %d\n",
+ __func__, result));
+ goto exit;
+ }
+
+ if (*entryEnd) {
+ LOG(10, (KERN_DEBUG "VMware hgfs: %s: end of dir reached\n", __func__));
+ goto exit;
+ }
+
+ /*
+ * Escape all non-printable characters (which for linux is just
+ * "/").
+ *
+ * Note that normally we would first need to convert from the
+ * CP name format, but that is done implicitely here since we
+ * are guaranteed to have just one path component per dentry.
+ */
+ result = HgfsEscape_Do(fileName, strlen(fileName),
+ entryNameBufLen, entryName);
+ kfree(fileName);
+ fileName = NULL;
+
+ /*
+ * Check the filename length.
+ *
+ * If the name is too long to be represented in linux, we simply
+ * skip it (i.e., that file is not visible to our filesystem).
+ *
+ * HgfsEscape_Do returns a negative value if the escaped
+ * output didn't fit in the specified output size, so we can
+ * just check its return value.
+ */
+ if (result < 0) {
+ /*
+ * XXX: Another area where a bad server could cause us to loop
+ * forever.
+ */
+ *entryIgnore = TRUE;
+ result = 0;
+ goto exit;
+ }
+
+ *entryNameLength = result;
+ result = 0;
+
+ /*
+ * It is unfortunate, but the HGFS server sends back '.' and ".."
+ * when we do a SearchRead. In an ideal world, these would be faked
+ * on the client, but it would be a real backwards-compatibility
+ * hassle to change the behavior at this point.
+ *
+ * So instead, we'll take the '.' and ".." and modify their inode
+ * numbers so they match what the client expects.
+ */
+ if (!strncmp(entryName, ".", sizeof ".")) {
+ if (!dotAndDotDotIgnore) {
+ *entryIno = file->f_dentry->d_inode->i_ino;
+ } else {
+ *entryIgnore = TRUE;
+ }
+ } else if (!strncmp(entryName, "..", sizeof "..")) {
+ if (!dotAndDotDotIgnore) {
+ *entryIno = compat_parent_ino(file->f_dentry);
+ } else {
+ *entryIgnore = TRUE;
+ }
+ } else {
+ *entryIno = HgfsGetFileInode(&entryAttrs, file->f_dentry->d_inode->i_sb);
+ }
+
+ if (*entryIgnore) {
+ goto exit;
+ }
+
+ /* Assign the correct dentry type. */
+ *entryType = HgfsGetFileType(&entryAttrs);
+
+exit:
+ return result;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsDoReaddir --
*
* Handle a readdir request. See details below if interested.
*
@@ -820,7 +1134,7 @@ HgfsDirOpen(struct inode *inode, // IN: Inode of the dir to open
*
* - Passing an inum of zero to filldir doesn't work. At a minimum,
* you have to make up a bogus inum for each dentry.
- * - Passing the correct d_type to filldir seems to be non-critical;
+ * - Passing the correct entryType to filldir seems to be non-critical;
* apparently most programs (such as ls) stat each file if they
* really want to know what type it is. However, passing the
* correct type means that ls doesn't bother calling stat on
@@ -839,24 +1153,20 @@ HgfsDirOpen(struct inode *inode, // IN: Inode of the dir to open
*/
static int
-HgfsReaddir(struct file *file, // IN: Directory to read from
- void *dirent, // OUT: Buffer to copy dentries into
- filldir_t filldir) // IN: Filler function
+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
{
- HgfsSuperInfo *si;
- HgfsAttrInfo attr;
- uint32 d_type; // type of dirent
- char *fileName = NULL;
- char *escName = NULL; // buf for escaped version of name
- size_t escNameLength = NAME_MAX + 1;
- int nameLength = 0;
+ char *entryName = NULL; // buf for escaped version of name
+ size_t entryNameBufLen = NAME_MAX + 1;
+ int entryNameLength = 0;
int result = 0;
- Bool done = FALSE;
- Bool isStale = FALSE;
- ino_t ino;
+ Bool entryEnd = FALSE;
ASSERT(file);
- ASSERT(dirent);
+ ASSERT(filldirCtx);
if (!file ||
!(file->f_dentry) ||
@@ -865,209 +1175,257 @@ HgfsReaddir(struct file *file, // IN: Directory to read from
return -EFAULT;
}
- ASSERT(file->f_dentry->d_inode->i_sb);
-
- si = HGFS_SB_TO_COMMON(file->f_dentry->d_inode->i_sb);
-
- LOG(6, (KERN_DEBUG "VMware hgfs: HgfsReaddir: dir with name %s, "
- "inum %lu, f_pos %Lu\n",
+ LOG(4, (KERN_DEBUG "VMware hgfs: %s(%s, inum %lu, pos %Lu)\n",
+ __func__,
file->f_dentry->d_name.name,
file->f_dentry->d_inode->i_ino,
- file->f_pos));
+ *entryPos));
/*
- * rm -rf 6.10+ breaks because it does following:
- * an 'fd = open()' on a directory, followed by unlinkat()
- * which removes an entry from the directory it and then
- * fdopendir(fd). We get a call on open() but not on fdopendir(),
- * which means that we do not reflect the action of unlinkat(),
- * and thus rm -rf gets confused and marking entry as unremovable.
- * Note that this problem exists because hgfsServer reads all
- * the directory entries at open(). Interested reader may look at
- * coreutils/src/remove.c file.
- *
- * So as a workaround, we ask the server to populate entries on
- * first readdir() call rather than opendir(). This effect is
- * achieved by closing and reopening the directory. Grrr!
- *
- * XXX We should get rid of this code when/if we remove the above
- * behavior from hgfsServer.
+ * Refresh entries if required. See rm -rf 6.10+ breaking issue.
*/
- isStale = FILE_GET_FI_P(file)->isStale;
- if (isStale) {
- result = HgfsPrivateDirReOpen(file);
- if (result) {
- return result;
- }
+ result = HgfsReaddirRefreshEntries(file);
+ if (result != 0) {
+ return result;
}
+
/*
* Some day when we're out of things to do we can move this to a slab
* allocator.
*/
- escName = kmalloc(escNameLength, GFP_KERNEL);
- if (!escName) {
+ entryName = kmalloc(entryNameBufLen, GFP_KERNEL);
+ if (entryName == NULL) {
LOG(4, (KERN_DEBUG "VMware hgfs: HgfsReaddir: out of memory allocating "
"escaped name buffer\n"));
- return -ENOMEM;
+ return -ENOMEM;
}
- while (1) {
- /*
- * Nonzero result = we failed to get valid reply from server.
- * Zero result:
- * - done == TRUE means we hit the end of the directory
- * - Otherwise, fileName has the name of the next dirent
- *
- */
-
- result = HgfsGetNextDirEntry(si,
- FILE_GET_FI_P(file)->handle,
- (uint32)file->f_pos,
- &attr,
- &fileName,
- &done);
- if (result == -ENAMETOOLONG) {
- /*
- * Skip dentry if its name is too long (see below).
- *
- * XXX: If a bad server sends us bad packets, we can loop here
- * forever, as I did while testing *grumble*. Maybe we should error
- * in that case.
- */
- file->f_pos++;
- continue;
- } else if (result) {
- /* Error */
- LOG(4, (KERN_DEBUG "VMware hgfs: HgfsReaddir: error "
- "getting dentry\n"));
- kfree(escName);
- return result;
- }
-
- if (done == TRUE) {
- LOG(6, (KERN_DEBUG "VMware hgfs: HgfsReaddir: end of dir reached\n"));
+ while (!entryEnd) {
+ Bool entryIgnore;
+ ino_t entryIno = 0;
+ uint32 entryType = DT_UNKNOWN;
+
+ result = HgfsReaddirNextEntry(file,
+ *entryPos,
+ dotAndDotDotIgnore,
+ entryNameBufLen,
+ entryName,
+ &entryNameLength,
+ &entryIno,
+ &entryType,
+ &entryIgnore,
+ &entryEnd);
+
+ if (result != 0) {
+ /* An error occurred retrieving the entry, so exit. */
break;
}
- /*
- * Escape all non-printable characters (which for linux is just
- * "/").
- *
- * Note that normally we would first need to convert from the
- * CP name format, but that is done implicitely here since we
- * are guaranteed to have just one path component per dentry.
- */
- result = HgfsEscape_Do(fileName, strlen(fileName),
- escNameLength, escName);
- kfree(fileName);
- fileName = NULL;
-
- /*
- * Check the filename length.
- *
- * If the name is too long to be represented in linux, we simply
- * skip it (i.e., that file is not visible to our filesystem) by
- * incrementing file->f_pos and repeating the loop to get the
- * next dentry.
- *
- * HgfsEscape_Do returns a negative value if the escaped
- * output didn't fit in the specified output size, so we can
- * just check its return value.
- */
- if (result < 0) {
- /*
- * XXX: Another area where a bad server could cause us to loop
- * forever.
- */
- file->f_pos++;
+ if (entryEnd) {
+ LOG(10, (KERN_DEBUG "VMware hgfs: %s: end of dir reached\n", __func__));
continue;
}
- nameLength = result;
-
- /* Assign the correct dentry type. */
- switch (attr.type) {
-
- case HGFS_FILE_TYPE_SYMLINK:
- d_type = DT_LNK;
- break;
-
- case HGFS_FILE_TYPE_REGULAR:
- d_type = DT_REG;
- break;
-
- case HGFS_FILE_TYPE_DIRECTORY:
- d_type = DT_DIR;
- break;
-
- default:
- /*
- * XXX Should never happen. I'd put NOT_IMPLEMENTED() here
- * but if the driver ever goes in the host it's probably not
- * a good idea for an attacker to be able to hang the host
- * simply by using a bogus file type in a reply. [bac]
- *
- * Well it happens! Refer bug 548177 for details. In short,
- * when the user deletes a share, we hit this code path.
- *
- */
- d_type = DT_UNKNOWN;
- break;
- }
-
- /*
- * It is unfortunate, but the HGFS server sends back '.' and ".."
- * when we do a SearchRead. In an ideal world, these would be faked
- * on the client, but it would be a real backwards-compatibility
- * hassle to change the behavior at this point.
- *
- * So instead, we'll take the '.' and ".." and modify their inode
- * numbers so they match what the client expects.
- */
- if (!strncmp(escName, ".", sizeof ".")) {
- ino = file->f_dentry->d_inode->i_ino;
- } else if (!strncmp(escName, "..", sizeof "..")) {
- ino = compat_parent_ino(file->f_dentry);
- } else {
- if (attr.mask & HGFS_ATTR_VALID_FILEID) {
- ino = attr.hostFileId;
- } else {
- ino = iunique(file->f_dentry->d_inode->i_sb,
- HGFS_RESERVED_INO);
- }
+ if (entryIgnore) {
+ *entryPos += 1;
+ continue;
}
/*
- * Call filldir for this dentry.
+ * Call the HGFS wrapper to the system fill function to set this dentry.
*/
- LOG(6, (KERN_DEBUG "VMware hgfs: HgfsReaddir: calling filldir "
- "with \"%s\", %u, %Lu\n", escName, nameLength, file->f_pos));
- result = filldir(dirent, /* filldir callback struct */
- escName, /* name of dirent */
- nameLength, /* length of name */
- file->f_pos, /* offset of dirent */
- ino, /* inode number (0 makes it not show) */
- d_type); /* type of dirent */
- if (result) {
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: dir_emit(%s, %u, %Lu)\n",
+ __func__, entryName, entryNameLength, *entryPos));
+ if (!HgfsReaddirFillEntry(filldirCb, /* filldir callback function */
+ filldirCtx, /* filldir callback struct */
+ entryName, /* name of dirent */
+ entryNameLength, /* length of name */
+ *entryPos, /* entry position */
+ entryIno, /* inode number (0 makes it not show) */
+ entryType)) { /* type of dirent */
/*
- * This means that filldir ran out of room in the user buffer
+ * This means that dir_emit ran out of room in the user buffer
* it was copying into; we just break out and return, but
* don't increment f_pos. So the next time the user calls
* getdents, this dentry will be requested again, will get
* retrieved again, and get copied properly to the user.
*/
+ result = 0;
break;
}
- file->f_pos++;
+ *entryPos += 1;
}
- LOG(6, (KERN_DEBUG "VMware hgfs: HgfsReaddir: finished\n"));
- kfree(escName);
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: return\n",__func__));
+ kfree(entryName);
return 0;
}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsReaddir --
+ *
+ * Handle a readdir request.
+ *
+ * Results:
+ * Returns zero on success, or an error on failure.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+HgfsReaddir(struct file *file, // IN:
+ struct dir_context *ctx) // IN:
+{
+ /* 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",
+ __func__, file->f_dentry->d_name.name, ctx->pos));
+ return 0;
+ }
+
+ /* It is sufficient to pass the context as it contains the filler function. */
+ return HgfsDoReaddir(file, TRUE, NULL, ctx, &ctx->pos);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsReaddirFillEntry --
+ *
+ * Fill a readdir entry.
+ *
+ * Failure means that fill ran out of room in the user buffer
+ * it was copying into.
+ *
+ * Results:
+ * Returns TRUE on success, or FALSE on failure.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Bool
+HgfsReaddirFillEntry(filldir_t filldirCb, // IN: System filler callback
+ void *filldirCtx, // IN/OUT: System filler context
+ char *entryName, // IN: entry name
+ uint32 entryNameLength, // IN: max name length
+ loff_t entryPos, // IN: position = (ctx-pos)
+ ino_t entryIno, // IN: inode entry number
+ uint32 entryType) // IN: entry type
+{
+ struct dir_context *ctx = filldirCtx;
+ Bool result;
+
+ ASSERT(filldirCb == NULL); /* Contained within the context structure. */
+ ASSERT(ctx != NULL);
+ ASSERT(ctx->pos == entryPos);
+ ASSERT(entryName != NULL);
+ ASSERT(entryNameLength != 0);
+
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: dir_emit(%s, %u, %Lu)\n",
+ __func__, entryName, entryNameLength, ctx->pos));
+
+ result = dir_emit(ctx, /* filldir callback struct */
+ entryName, /* name of dirent */
+ entryNameLength, /* length of name */
+ entryIno, /* inode number (0 makes it not show) */
+ entryType); /* type of dirent */
+ return result;
+}
+#else
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsReaddir --
+ *
+ * Handle a readdir request.
+ *
+ * Results:
+ * Returns zero on success, or an error on failure.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+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);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * HgfsReaddirFillEntry --
+ *
+ * Fill a readdir entry.
+ *
+ * Failure means that fill ran out of room in the user buffer
+ * it was copying into.
+ *
+ * Results:
+ * Returns TRUE on success, or FALSE on failure.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Bool
+HgfsReaddirFillEntry(filldir_t filldirCb, // IN: System filler callback
+ void *filldirCtx, // IN/OUT: System filler context
+ char *entryName, // IN: entry name
+ uint32 entryNameLength, // IN: max name length
+ loff_t entryPos, // IN: position
+ ino_t entryIno, // IN: inode entry number
+ uint32 entryType) // IN: entry type
+{
+ Bool result = TRUE;
+ int fillResult;
+
+ ASSERT(filldirCb != NULL);
+ ASSERT(filldirCtx != NULL);
+ ASSERT(entryName != NULL);
+ ASSERT(entryNameLength != 0);
+
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: calling filldir(%s, %u, %Lu\n",
+ __func__, entryName, entryNameLength, entryPos));
+
+ fillResult = filldirCb(filldirCtx, /* filldir callback struct */
+ entryName, /* name of dirent */
+ entryNameLength, /* length of name */
+ entryPos, /* offset of dirent */
+ entryIno, /* inode number (0 makes it not show) */
+ entryType); /* type of dirent */
+
+ if (fillResult != 0) {
+ result = FALSE;
+ }
+ LOG(6, (KERN_DEBUG "VMware hgfs: %s: return %d\n", __func__, result));
+ return result;
+}
+#endif
+
+
/*
*----------------------------------------------------------------------
*