[Bug 6069] Add a fstatvfs function for libsmbclient
authorDerrell Lipman <derrell.lipman@unwireduniverse.com>
Thu, 12 Feb 2009 15:39:17 +0000 (10:39 -0500)
committerDerrell Lipman <derrell.lipman@unwireduniverse.com>
Thu, 12 Feb 2009 15:39:48 +0000 (10:39 -0500)
- port functionality from v3_3_test to master

Derrell

12 files changed:
examples/libsmbclient/Makefile
examples/libsmbclient/testfstatvfs.c [new file with mode: 0644]
examples/libsmbclient/teststatvfs.c [new file with mode: 0644]
source3/include/includes.h
source3/include/libsmb_internal.h
source3/include/libsmbclient.h
source3/include/proto.h
source3/libsmb/clifsinfo.c
source3/libsmb/libsmb_compat.c
source3/libsmb/libsmb_context.c
source3/libsmb/libsmb_setget.c
source3/libsmb/libsmb_stat.c

index 047addc8f7ad856e9c865d117cba7b0bd00aebd1..728dbe0edbca9597afe3b24aa95b9ce83a0ce664 100644 (file)
@@ -24,6 +24,8 @@ TESTS=        testsmbc \
        teststat \
        teststat2 \
        teststat3 \
+       teststatvfs \
+       testfstatvfs \
        testtruncate \
        testchmod \
        testutime \
@@ -74,6 +76,14 @@ teststat3: teststat3.o
        @echo Linking teststat3
        $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBSMBCLIENT) -lpopt
 
+teststatvfs: teststatvfs.o
+       @echo Linking teststatvfs
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBSMBCLIENT) -lpopt
+
+testfstatvfs: testfstatvfs.o
+       @echo Linking testfstatvfs
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBSMBCLIENT) -lpopt
+
 testtruncate: testtruncate.o
        @echo Linking testtruncate
        $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBSMBCLIENT) -lpopt
diff --git a/examples/libsmbclient/testfstatvfs.c b/examples/libsmbclient/testfstatvfs.c
new file mode 100644 (file)
index 0000000..b4dafef
--- /dev/null
@@ -0,0 +1,122 @@
+#include <sys/types.h>
+#include <sys/statvfs.h>
+#include <stdio.h> 
+#include <unistd.h>
+#include <string.h> 
+#include <time.h> 
+#include <errno.h>
+#include <libsmbclient.h> 
+#include "get_auth_data_fn.h"
+
+
+int main(int argc, char * argv[]) 
+{ 
+    int             i;
+    int             fd;
+    int             ret;
+    int             debug = 0;
+    char *          p;
+    char            path[2048];
+    struct stat     statbuf;
+    struct statvfs  statvfsbuf;
+    
+    smbc_init(get_auth_data_fn, debug); 
+    
+    for (;;)
+    {
+        fprintf(stdout, "Path: ");
+        *path = '\0';
+        fgets(path, sizeof(path) - 1, stdin);
+        if (strlen(path) == 0)
+        {
+            return 0;
+        }
+
+        p = path + strlen(path) - 1;
+        if (*p == '\n')
+        {
+            *p = '\0';
+        }
+    
+        /* Determine if it's a file or a folder */
+        if (smbc_stat(path, &statbuf) < 0)
+        {
+            perror("smbc_stat");
+            continue;
+        }
+
+        if (S_ISREG(statbuf.st_mode))
+        {
+            if ((fd = smbc_open(path, O_RDONLY, 0)) < 0)
+            {
+                perror("smbc_open");
+                continue;
+            }
+        }
+        else
+        {
+            if ((fd = smbc_opendir(path)) < 0)
+            {
+                perror("smbc_opendir");
+                continue;
+            }
+        }
+
+        ret = smbc_fstatvfs(fd, &statvfsbuf);
+
+        smbc_close(fd);
+
+        if (ret < 0)
+        {
+            perror("fstatvfs");
+        }
+        else
+        {
+            printf("\n");
+            printf("Block Size: %lu\n", statvfsbuf.f_bsize);
+            printf("Fragment Size: %lu\n", statvfsbuf.f_frsize);
+            printf("Blocks: %llu\n", statvfsbuf.f_blocks);
+            printf("Free Blocks: %llu\n", statvfsbuf.f_bfree);
+            printf("Available Blocks: %llu\n", statvfsbuf.f_bavail);
+            printf("Files : %llu\n", statvfsbuf.f_files);
+            printf("Free Files: %llu\n", statvfsbuf.f_ffree);
+            printf("Available Files: %llu\n", statvfsbuf.f_favail);
+            printf("File System ID: %lu\n", statvfsbuf.f_fsid);
+            printf("\n");
+
+            printf("Flags: 0x%lx\n", statvfsbuf.f_flag);
+            printf("Extended Features: ");
+
+            if (statvfsbuf.f_flag & SMBC_VFS_FEATURE_NO_UNIXCIFS)
+            {
+                printf("NO_UNIXCIFS ");
+            }
+            else
+            {
+                printf("unixcifs ");
+            }
+
+            if (statvfsbuf.f_flag & SMBC_VFS_FEATURE_CASE_INSENSITIVE)
+            {
+                printf("CASE_INSENSITIVE ");
+            }
+            else
+            {
+                printf("case_sensitive ");
+            }
+
+            if (statvfsbuf.f_flag & SMBC_VFS_FEATURE_DFS)
+            {
+                printf("DFS ");
+            }
+            else
+            {
+                printf("no_dfs ");
+            }
+
+            printf("\n");
+        }
+    }
+
+    return 0; 
+}
diff --git a/examples/libsmbclient/teststatvfs.c b/examples/libsmbclient/teststatvfs.c
new file mode 100644 (file)
index 0000000..8812002
--- /dev/null
@@ -0,0 +1,96 @@
+#include <sys/types.h>
+#include <sys/statvfs.h>
+#include <stdio.h> 
+#include <unistd.h>
+#include <string.h> 
+#include <time.h> 
+#include <errno.h>
+#include <libsmbclient.h> 
+#include "get_auth_data_fn.h"
+
+
+int main(int argc, char * argv[]) 
+{ 
+    int             i;
+    int             fd;
+    int             ret;
+    int             debug = 0;
+    char *          p;
+    char            path[2048];
+    struct stat     statbuf;
+    struct statvfs  statvfsbuf;
+    
+    smbc_init(get_auth_data_fn, debug); 
+    
+    for (;;)
+    {
+        fprintf(stdout, "Path: ");
+        *path = '\0';
+        fgets(path, sizeof(path) - 1, stdin);
+        if (strlen(path) == 0)
+        {
+            return 0;
+        }
+
+        p = path + strlen(path) - 1;
+        if (*p == '\n')
+        {
+            *p = '\0';
+        }
+    
+        ret = smbc_statvfs(path, &statvfsbuf);
+
+        if (ret < 0)
+        {
+            perror("fstatvfs");
+        }
+        else
+        {
+            printf("\n");
+            printf("Block Size: %lu\n", statvfsbuf.f_bsize);
+            printf("Fragment Size: %lu\n", statvfsbuf.f_frsize);
+            printf("Blocks: %llu\n", statvfsbuf.f_blocks);
+            printf("Free Blocks: %llu\n", statvfsbuf.f_bfree);
+            printf("Available Blocks: %llu\n", statvfsbuf.f_bavail);
+            printf("Files : %llu\n", statvfsbuf.f_files);
+            printf("Free Files: %llu\n", statvfsbuf.f_ffree);
+            printf("Available Files: %llu\n", statvfsbuf.f_favail);
+            printf("File System ID: %lu\n", statvfsbuf.f_fsid);
+            printf("\n");
+
+            printf("Flags: 0x%lx\n", statvfsbuf.f_flag);
+            printf("Extended Features: ");
+
+            if (statvfsbuf.f_flag & SMBC_VFS_FEATURE_NO_UNIXCIFS)
+            {
+                printf("NO_UNIXCIFS ");
+            }
+            else
+            {
+                printf("unixcifs ");
+            }
+
+            if (statvfsbuf.f_flag & SMBC_VFS_FEATURE_CASE_INSENSITIVE)
+            {
+                printf("CASE_INSENSITIVE ");
+            }
+            else
+            {
+                printf("case_sensitive ");
+            }
+
+            if (statvfsbuf.f_flag & SMBC_VFS_FEATURE_DFS)
+            {
+                printf("DFS ");
+            }
+            else
+            {
+                printf("no_dfs ");
+            }
+
+            printf("\n");
+        }
+    }
+
+    return 0; 
+}
index fc77534402aa11393618cead360b183b46e44494..2c033e8b69ae26ef8e7420e25885871efcb803aa 100644 (file)
@@ -396,6 +396,7 @@ typedef sig_atomic_t volatile SIG_ATOMIC_T;
 #endif
 
 #define SBIG_UINT(p, ofs, v) (SIVAL(p,ofs,(v)&0xFFFFFFFF), SIVAL(p,(ofs)+4,(v)>>32))
+#define BIG_UINT(p, ofs) ((((uint64_t) IVAL(p,(ofs)+4))<<32)|IVAL(p,ofs))
 #define IVAL2_TO_SMB_BIG_UINT(buf,off) ( (((uint64_t)(IVAL((buf),(off)))) & ((uint64_t)0xFFFFFFFF)) | \
                (( ((uint64_t)(IVAL((buf),(off+4)))) & ((uint64_t)0xFFFFFFFF) ) << 32 ) )
 
index 67add074bfc15bc3982447e06f75887b787a64f2..166685c38015646509f8dde8c2906c7dc78cbbb2 100644 (file)
@@ -197,6 +197,8 @@ struct SMBC_internal_data {
                 smbc_stat_fn                    stat_fn;
                 smbc_fstat_fn                   fstat_fn;
 #endif
+                smbc_statvfs_fn                 statvfs_fn;
+                smbc_fstatvfs_fn                fstatvfs_fn;
                 smbc_ftruncate_fn               ftruncate_fn;
 #if 0 /* Left in libsmbclient.h for backward compatibility */
                 smbc_close_fn                   close_fn;
@@ -501,6 +503,18 @@ SMBC_fstat_ctx(SMBCCTX *context,
                struct stat *st);
 
 
+int
+SMBC_statvfs_ctx(SMBCCTX *context,
+                 char *path,
+                 struct statvfs *st);
+
+
+int
+SMBC_fstatvfs_ctx(SMBCCTX *context,
+                  SMBCFILE *file,
+                  struct statvfs *st);
+
+
 /* Functions in libsmb_xattr.c */
 int
 SMBC_setxattr_ctx(SMBCCTX *context,
index b2d9483a0b9ad9098a7ed143dae521ec0012aaf4..d35d9de6eaffecdd473bbe0827e1697a124f885d 100644 (file)
@@ -75,6 +75,7 @@ extern "C" {
 /* Make sure we have the following includes for now ... */
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/statvfs.h>
 #include <fcntl.h>
 #include <utime.h>
 
@@ -174,6 +175,22 @@ typedef enum smbc_smb_encrypt_level
 } smbc_smb_encrypt_level;
 
 
+/**
+ * Capabilities set in the f_flag field of struct statvfs, from
+ * smbc_statvfs(). These may be OR-ed together to reflect a full set of
+ * available capabilities.
+ */
+typedef enum smbc_vfs_feature
+{
+    /* Defined by POSIX or in Linux include files (low-order bits) */
+    SMBC_VFS_FEATURE_RDONLY         = (1 << 0),
+
+    /* Specific to libsmbclient (high-order bits) */
+    SMBC_VFS_FEATURE_DFS              = (1 << 29),
+    SMBC_VFS_FEATURE_CASE_INSENSITIVE = (1 << 30),
+    SMBC_VFS_FEATURE_NO_UNIXCIFS      = (1 << 31)
+} smbc_vfs_feature;
+
 typedef int smbc_bool;
 
 
@@ -853,6 +870,18 @@ typedef int (*smbc_fstat_fn)(SMBCCTX *c,
 smbc_fstat_fn smbc_getFunctionFstat(SMBCCTX *c);
 void smbc_setFunctionFstat(SMBCCTX *c, smbc_fstat_fn fn);
 
+typedef int (*smbc_statvfs_fn)(SMBCCTX *c,
+                               char *path,
+                               struct statvfs *st);
+smbc_statvfs_fn smbc_getFunctionStatVFS(SMBCCTX *c);
+void smbc_setFunctionStatVFS(SMBCCTX *c, smbc_statvfs_fn fn);
+
+typedef int (*smbc_fstatvfs_fn)(SMBCCTX *c,
+                                SMBCFILE *file,
+                                struct statvfs *st);
+smbc_fstatvfs_fn smbc_getFunctionFstatVFS(SMBCCTX *c);
+void smbc_setFunctionFstatVFS(SMBCCTX *c, smbc_fstatvfs_fn fn);
+
 typedef int (*smbc_ftruncate_fn)(SMBCCTX *c,
                                  SMBCFILE *f,
                                  off_t size);
@@ -1591,6 +1620,52 @@ int smbc_stat(const char *url, struct stat *st);
 int smbc_fstat(int fd, struct stat *st);
 
 
+/**@ingroup attribute
+ * Get file system information for a specified path.
+ * 
+ * @param url       The smb url to get information for
+ *
+ * @param st        pointer to a buffer that will be filled with 
+ *                  standard Unix struct statvfs information.
+ * 
+ * @return          EBADF  filedes is bad.
+ *                  - EACCES Permission denied.
+ *                  - EBADF fd is not a valid file descriptor
+ *                  - EINVAL Problems occurred in the underlying routines
+ *                   or smbc_init not called.
+ *                  - ENOMEM Out of memory
+ *
+ * @see             Unix fstatvfs()
+ *
+ */
+int
+smbc_statvfs(char *url,
+             struct statvfs *st);
+
+/**@ingroup attribute
+ * Get file system information via an file descriptor.
+ * 
+ * @param fd        Open file handle from smbc_open(), smbc_creat(),
+ *                  or smbc_opendir()
+ *
+ * @param st        pointer to a buffer that will be filled with 
+ *                  standard Unix struct statvfs information.
+ * 
+ * @return          EBADF  filedes is bad.
+ *                  - EACCES Permission denied.
+ *                  - EBADF fd is not a valid file descriptor
+ *                  - EINVAL Problems occurred in the underlying routines
+ *                   or smbc_init not called.
+ *                  - ENOMEM Out of memory
+ *
+ * @see             Unix fstatvfs()
+ *
+ */
+int
+smbc_fstatvfs(int fd,
+              struct statvfs *st);
+
+
 /**@ingroup attribute
  * Truncate a file given a file descriptor
  * 
index 3478ea4f854b99efac99bc22dcc977f49f2cae8c..1b17e0246a9f6fdbce3877fe8dfc075b34b98cd0 100644 (file)
@@ -2606,6 +2606,21 @@ bool cli_set_unix_extensions_capabilities(struct cli_state *cli, uint16 major, u
 bool cli_get_fs_attr_info(struct cli_state *cli, uint32 *fs_attr);
 bool cli_get_fs_volume_info_old(struct cli_state *cli, fstring volume_name, uint32 *pserial_number);
 bool cli_get_fs_volume_info(struct cli_state *cli, fstring volume_name, uint32 *pserial_number, time_t *pdate);
+bool cli_get_fs_full_size_info(struct cli_state *cli,
+                               uint64_t *total_allocation_units,
+                               uint64_t *caller_allocation_units,
+                               uint64_t *actual_allocation_units,
+                               uint64_t *sectors_per_allocation_unit,
+                               uint64_t *bytes_per_sector);
+bool cli_get_posix_fs_info(struct cli_state *cli,
+                           uint32 *optimal_transfer_size,
+                           uint32 *block_size,
+                           uint64_t *total_blocks,
+                           uint64_t *blocks_available,
+                           uint64_t *user_blocks_available,
+                           uint64_t *total_file_nodes,
+                           uint64_t *free_file_nodes,
+                           uint64_t *fs_identifier);
 NTSTATUS cli_raw_ntlm_smb_encryption_start(struct cli_state *cli, 
                                const char *user,
                                const char *pass,
index 77290d2df9b3c2c739264a8aa239756ff3c6fcf4..e0ae948aaf47f2034ed8dc3117e28f7de44d4b29 100644 (file)
@@ -305,6 +305,151 @@ cleanup:
        return ret;
 }
 
+bool cli_get_fs_full_size_info(struct cli_state *cli,
+                               uint64_t *total_allocation_units,
+                               uint64_t *caller_allocation_units,
+                               uint64_t *actual_allocation_units,
+                               uint64_t *sectors_per_allocation_unit,
+                               uint64_t *bytes_per_sector)
+{
+       bool ret = False;
+       uint16 setup;
+       char param[2];
+       char *rparam=NULL, *rdata=NULL;
+       unsigned int rparam_count=0, rdata_count=0;
+
+       setup = TRANSACT2_QFSINFO;
+
+       SSVAL(param,0,SMB_FS_FULL_SIZE_INFORMATION);
+
+       if (!cli_send_trans(cli, SMBtrans2,
+                   NULL,
+                   0, 0,
+                   &setup, 1, 0,
+                   param, 2, 0,
+                   NULL, 0, 560)) {
+               goto cleanup;
+       }
+
+       if (!cli_receive_trans(cli, SMBtrans2,
+                              &rparam, &rparam_count,
+                              &rdata, &rdata_count)) {
+               goto cleanup;
+       }
+
+       if (cli_is_error(cli)) {
+               ret = False;
+               goto cleanup;
+       } else {
+               ret = True;
+       }
+
+       if (rdata_count != 32) {
+               goto cleanup;
+       }
+
+       if (total_allocation_units) {
+                *total_allocation_units = BIG_UINT(rdata, 0);
+       }
+       if (caller_allocation_units) {
+               *caller_allocation_units = BIG_UINT(rdata,8);
+       }
+       if (actual_allocation_units) {
+               *actual_allocation_units = BIG_UINT(rdata,16);
+       }
+       if (sectors_per_allocation_unit) {
+               *sectors_per_allocation_unit = IVAL(rdata,24);
+       }
+       if (bytes_per_sector) {
+               *bytes_per_sector = IVAL(rdata,28);
+       }
+
+cleanup:
+       SAFE_FREE(rparam);
+       SAFE_FREE(rdata);
+
+       return ret;
+}
+
+bool cli_get_posix_fs_info(struct cli_state *cli,
+                           uint32 *optimal_transfer_size,
+                           uint32 *block_size,
+                           uint64_t *total_blocks,
+                           uint64_t *blocks_available,
+                           uint64_t *user_blocks_available,
+                           uint64_t *total_file_nodes,
+                           uint64_t *free_file_nodes,
+                           uint64_t *fs_identifier)
+{
+       bool ret = False;
+       uint16 setup;
+       char param[2];
+       char *rparam=NULL, *rdata=NULL;
+       unsigned int rparam_count=0, rdata_count=0;
+
+       setup = TRANSACT2_QFSINFO;
+
+       SSVAL(param,0,SMB_QUERY_POSIX_FS_INFO);
+
+       if (!cli_send_trans(cli, SMBtrans2,
+                   NULL,
+                   0, 0,
+                   &setup, 1, 0,
+                   param, 2, 0,
+                   NULL, 0, 560)) {
+               goto cleanup;
+       }
+
+       if (!cli_receive_trans(cli, SMBtrans2,
+                              &rparam, &rparam_count,
+                              &rdata, &rdata_count)) {
+               goto cleanup;
+       }
+
+       if (cli_is_error(cli)) {
+               ret = False;
+               goto cleanup;
+       } else {
+               ret = True;
+       }
+
+       if (rdata_count != 56) {
+               goto cleanup;
+       }
+
+       if (optimal_transfer_size) {
+                *optimal_transfer_size = IVAL(rdata, 0);
+       }
+       if (block_size) {
+               *block_size = IVAL(rdata,4);
+       }
+       if (total_blocks) {
+               *total_blocks = BIG_UINT(rdata,8);
+       }
+       if (blocks_available) {
+               *blocks_available = BIG_UINT(rdata,16);
+       }
+       if (user_blocks_available) {
+               *user_blocks_available = BIG_UINT(rdata,24);
+       }
+       if (total_file_nodes) {
+               *total_file_nodes = BIG_UINT(rdata,32);
+       }
+       if (free_file_nodes) {
+               *free_file_nodes = BIG_UINT(rdata,40);
+       }
+       if (fs_identifier) {
+               *fs_identifier = BIG_UINT(rdata,48);
+       }
+
+cleanup:
+       SAFE_FREE(rparam);
+       SAFE_FREE(rdata);
+
+       return ret;
+}
+
+
 /******************************************************************************
  Send/receive the request encryption blob.
 ******************************************************************************/
index ad8fd922db2b6da1dcb0412a3cb84ba044d1f196..56d113f31a221736c07454f2e7154b8711185def 100644 (file)
@@ -329,6 +329,21 @@ smbc_fstat(int fd,
         return smbc_getFunctionFstat(statcont)(statcont, file, st);
 }
 
+int
+smbc_statvfs(char *path,
+             struct statvfs *st)
+{
+        return smbc_getFunctionStatVFS(statcont)(statcont, path, st);
+}
+
+int
+smbc_fstatvfs(int fd,
+              struct statvfs *st)
+{
+       SMBCFILE * file = find_fd(fd);
+        return smbc_getFunctionFstatVFS(statcont)(statcont, file, st);
+}
+
 int
 smbc_ftruncate(int fd,
                off_t size)
index c2c33e53025903c6a3a2ff02c4cd781f8fdebee2..c1af48507c8adaee15482c4ff1c715e978baebf2 100644 (file)
@@ -94,6 +94,8 @@ smbc_new_context(void)
         smbc_setFunctionLseek(context, SMBC_lseek_ctx);
         smbc_setFunctionFtruncate(context, SMBC_ftruncate_ctx);
         smbc_setFunctionStat(context, SMBC_stat_ctx);
+        smbc_setFunctionStatVFS(context, SMBC_statvfs_ctx);
+        smbc_setFunctionFstatVFS(context, SMBC_fstatvfs_ctx);
         smbc_setFunctionFstat(context, SMBC_fstat_ctx);
         smbc_setFunctionOpendir(context, SMBC_opendir_ctx);
         smbc_setFunctionClosedir(context, SMBC_closedir_ctx);
index 9de49a5b3f6dca753fb9be9f403c1ce3db95a310..3493e4f8dd2f726c631703cf4122323828d2e8c3 100644 (file)
@@ -659,6 +659,30 @@ smbc_setFunctionFstat(SMBCCTX *c, smbc_fstat_fn fn)
         c->fstat = fn;
 }
 
+smbc_statvfs_fn
+smbc_getFunctionStatVFS(SMBCCTX *c)
+{
+        return c->internal->posix_emu.statvfs_fn;
+}
+
+void
+smbc_setFunctionStatVFS(SMBCCTX *c, smbc_statvfs_fn fn)
+{
+        c->internal->posix_emu.statvfs_fn = fn;
+}
+
+smbc_fstatvfs_fn
+smbc_getFunctionFstatVFS(SMBCCTX *c)
+{
+        return c->internal->posix_emu.fstatvfs_fn;
+}
+
+void
+smbc_setFunctionFstatVFS(SMBCCTX *c, smbc_fstatvfs_fn fn)
+{
+        c->internal->posix_emu.fstatvfs_fn = fn;
+}
+
 smbc_ftruncate_fn
 smbc_getFunctionFtruncate(SMBCCTX *c)
 {
index 27546f687ed3f1343f1713464673af9a398bbcbf..d589f7ef712b61b32a9b157f0eacdbd3de1e5fed 100644 (file)
@@ -300,3 +300,172 @@ SMBC_fstat_ctx(SMBCCTX *context,
        return 0;
         
 }
+
+
+/*
+ * Routine to obtain file system information given a path
+ */
+int
+SMBC_statvfs_ctx(SMBCCTX *context,
+                 char *path,
+                 struct statvfs *st)
+{
+        int             ret;
+        bool            bIsDir;
+        struct stat     statbuf;
+        SMBCFILE *      pFile;
+
+        /* Determine if the provided path is a file or a folder */
+        if (SMBC_stat_ctx(context, path, &statbuf) < 0) {
+                return -1;
+        }
+
+        /* Is it a file or a directory?  */
+        if (S_ISDIR(statbuf.st_mode)) {
+                /* It's a directory. */
+                if ((pFile = SMBC_opendir_ctx(context, path)) < 0) {
+                        return -1;
+                }
+                bIsDir = true;
+        } else if (S_ISREG(statbuf.st_mode)) {
+                /* It's a file. */
+                if ((pFile = SMBC_open_ctx(context, path, O_RDONLY, 0)) < 0) {
+                        return -1;
+                }
+                bIsDir = false;
+        } else {
+                /* It's neither a file nor a directory. Not supported. */
+                errno = ENOSYS;
+                return -1;
+        }
+
+        /* Now we have an open file handle, so just use SMBC_fstatvfs */
+        ret = SMBC_fstatvfs_ctx(context, pFile, st);
+
+        /* Close the file or directory */
+        if (bIsDir) {
+                SMBC_closedir_ctx(context, pFile);
+        } else {
+                SMBC_close_ctx(context, pFile);
+        }
+
+        return ret;
+}
+
+
+/*
+ * Routine to obtain file system information given an fd
+ */
+
+int
+SMBC_fstatvfs_ctx(SMBCCTX *context,
+                  SMBCFILE *file,
+                  struct statvfs *st)
+{
+       uint32 fs_attrs = 0;
+       struct cli_state *cli = file->srv->cli;
+                
+
+        /* Initialize all fields (at least until we actually use them) */
+        memset(st, 0, sizeof(*st));
+
+        /*
+         * The state of each flag is such that the same bits are unset as
+         * would typically be unset on a local file system on a POSIX OS. Thus
+         * the bit is on, for example, only for case-insensitive file systems
+         * since most POSIX file systems are case sensitive and fstatvfs()
+         * would typically return zero in these bits on such a local file
+         * system.
+         */
+
+        /* See if the server has UNIX CIFS support */
+        if (! SERVER_HAS_UNIX_CIFS(cli)) {
+                uint64_t total_allocation_units;
+                uint64_t caller_allocation_units;
+                uint64_t actual_allocation_units;
+                uint64_t sectors_per_allocation_unit;
+                uint64_t bytes_per_sector;
+                
+                /* Nope. If size data is available... */
+                if (cli_get_fs_full_size_info(cli,
+                                              &total_allocation_units,
+                                              &caller_allocation_units,
+                                              &actual_allocation_units,
+                                              &sectors_per_allocation_unit,
+                                              &bytes_per_sector)) {
+
+                        /* ... then provide it */
+                        st->f_bsize =
+                                (unsigned long) bytes_per_sector;
+                        st->f_frsize =
+                                (unsigned long) sectors_per_allocation_unit;
+                        st->f_blocks =
+                                (fsblkcnt_t) total_allocation_units;
+                        st->f_bfree =
+                                (fsblkcnt_t) actual_allocation_units;
+                }
+
+                st->f_flag |= SMBC_VFS_FEATURE_NO_UNIXCIFS;
+        } else {
+                uint32 optimal_transfer_size;
+                uint32 block_size;
+                uint64_t total_blocks;
+                uint64_t blocks_available;
+                uint64_t user_blocks_available;
+                uint64_t total_file_nodes;
+                uint64_t free_file_nodes;
+                uint64_t fs_identifier;
+
+                /* Has UNIXCIFS. If POSIX filesystem info is available... */
+                if (cli_get_posix_fs_info(cli,
+                                          &optimal_transfer_size,
+                                          &block_size,
+                                          &total_blocks,
+                                          &blocks_available,
+                                          &user_blocks_available,
+                                          &total_file_nodes,
+                                          &free_file_nodes,
+                                          &fs_identifier)) {
+
+                        /* ... then what's provided here takes precedence. */
+                        st->f_bsize =
+                                (unsigned long) block_size;
+                        st->f_blocks =
+                                (fsblkcnt_t) total_blocks;
+                        st->f_bfree =
+                                (fsblkcnt_t) blocks_available;
+                        st->f_bavail =
+                                (fsblkcnt_t) user_blocks_available;
+                        st->f_files =
+                                (fsfilcnt_t) total_file_nodes;
+                        st->f_ffree =
+                                (fsfilcnt_t) free_file_nodes;
+                        st->f_fsid =
+                                (unsigned long) fs_identifier;
+                        
+                }
+        }
+
+        /* See if the share is case sensitive */
+        if (!cli_get_fs_attr_info(cli, &fs_attrs)) {
+                /*
+                 * We can't determine the case sensitivity of
+                 * the share. We have no choice but to use the
+                 * user-specified case sensitivity setting.
+                 */
+                if (! smbc_getOptionCaseSensitive(context)) {
+                        st->f_flag |= SMBC_VFS_FEATURE_CASE_INSENSITIVE;
+                }
+        } else {
+                if (! (fs_attrs & FILE_CASE_SENSITIVE_SEARCH)) {
+                        st->f_flag |= SMBC_VFS_FEATURE_CASE_INSENSITIVE;
+                }
+        }
+
+        /* See if DFS is supported */
+       if ((cli->capabilities & CAP_DFS) &&  cli->dfsroot) {
+                st->f_flag |= SMBC_VFS_FEATURE_DFS;
+        }
+
+        return 0;
+}