From ae259575c447e61665c8e7070c476914161b953f Mon Sep 17 00:00:00 2001 From: Derrell Lipman Date: Thu, 12 Feb 2009 10:39:17 -0500 Subject: [Bug 6069] Add a fstatvfs function for libsmbclient - port functionality from v3_3_test to master Derrell --- source3/libsmb/clifsinfo.c | 145 ++++++++++++++++++++++++++++++++++ source3/libsmb/libsmb_compat.c | 15 ++++ source3/libsmb/libsmb_context.c | 2 + source3/libsmb/libsmb_setget.c | 24 ++++++ source3/libsmb/libsmb_stat.c | 169 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 355 insertions(+) (limited to 'source3/libsmb') diff --git a/source3/libsmb/clifsinfo.c b/source3/libsmb/clifsinfo.c index 77290d2df9..e0ae948aaf 100644 --- a/source3/libsmb/clifsinfo.c +++ b/source3/libsmb/clifsinfo.c @@ -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. ******************************************************************************/ diff --git a/source3/libsmb/libsmb_compat.c b/source3/libsmb/libsmb_compat.c index ad8fd922db..56d113f31a 100644 --- a/source3/libsmb/libsmb_compat.c +++ b/source3/libsmb/libsmb_compat.c @@ -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) diff --git a/source3/libsmb/libsmb_context.c b/source3/libsmb/libsmb_context.c index c2c33e5302..c1af48507c 100644 --- a/source3/libsmb/libsmb_context.c +++ b/source3/libsmb/libsmb_context.c @@ -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); diff --git a/source3/libsmb/libsmb_setget.c b/source3/libsmb/libsmb_setget.c index 9de49a5b3f..3493e4f8dd 100644 --- a/source3/libsmb/libsmb_setget.c +++ b/source3/libsmb/libsmb_setget.c @@ -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) { diff --git a/source3/libsmb/libsmb_stat.c b/source3/libsmb/libsmb_stat.c index 27546f687e..d589f7ef71 100644 --- a/source3/libsmb/libsmb_stat.c +++ b/source3/libsmb/libsmb_stat.c @@ -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, + §ors_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; +} -- cgit