#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/statvfs.h>
#include <sys/types.h>
#include <time.h>
#include <pthread.h>
#include <libsmbclient.h>
#include <glib.h>
#include "common.h"
#include "smbitem.h"
#include "samba.h"
#include "stat_workaround.h"
#include "function.h"
static int function_free_space_size = 0;
static int function_quiet_flag = 1;
static int function_show_dollar_shares = 0;
static int function_show_hidden_hosts = 0;
static int function_noexec_attr = 0;
int function_set_free_space_size(int blocks_count){
DPRINTF(7, "blocks_count=%d\n", blocks_count);
g_atomic_int_set(&function_free_space_size, blocks_count);
return 1;
}
static int function_get_free_space_size(void){
return g_atomic_int_get(&function_free_space_size);
}
int function_set_quiet_flag(int flag){
DPRINTF(7, "flag=%d\n", flag);
g_atomic_int_set(&function_quiet_flag, flag);
return 1;
}
static inline int function_get_quiet_flag(void){
return g_atomic_int_get(&function_quiet_flag);
}
int function_set_dollar_share_visibility(int flag){
DPRINTF(7, "flag=%d\n", flag);
g_atomic_int_set(&function_show_dollar_shares, flag);
return 1;
}
static inline int function_get_dollar_share_visibility(void){
return g_atomic_int_get(&function_show_dollar_shares);
}
int function_set_hidden_hosts_visibility(int flag){
DPRINTF(7, "flag=%d\n", flag);
g_atomic_int_set(&function_show_hidden_hosts, flag);
return 1;
}
static inline int function_get_hidden_hosts_visibility(void){
return g_atomic_int_get(&function_show_hidden_hosts);
}
int function_set_noexec_attr(int flag){
DPRINTF(7, "flag=%d\n", flag);
g_atomic_int_set(&function_noexec_attr, flag);
return 1;
}
static inline int function_get_noexec_attr(void){
return g_atomic_int_get(&function_noexec_attr);
}
static inline int function_check_xattr_name(const char *name){
static char *xattr_name = "system.nt_sec_desc.";
return (strncmp(name, xattr_name, strlen(xattr_name)) == 0);
}
static inline samba_fd function_get_fd(struct fuse_file_info *fi){
return (samba_fd) fi->fh;
}
static inline void function_store_fd(struct fuse_file_info *fi, samba_fd fd){
fi->fh = (uint64_t) fd;
}
static int function_open(const char *path, struct fuse_file_info *fi){
samba_fd fd;
DPRINTF(5, "(%s, flags=%o, fh=%llx)\n", path, fi->flags, (long long) fi->fh);
if (smbitem_what_is(path) != SMBITEM_SMB_SHARE_ITEM) return -EINVAL;
if ((fd = samba_open(path, fi->flags, 0777)) == NULL) return -errno;
function_store_fd(fi, fd);
return 0;
}
static int function_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi){
int result;
samba_fd fd;
DPRINTF(5, "(%s, %zd, fh=%llx, offset=%lld, flags=%o)\n", path, size,
(long long) fi->fh, (long long) offset, fi->flags);
if ((fd = function_get_fd(fi)) == NULL) return -EBADF;
result = samba_read(fd, offset, buf, size);
if (result == (ssize_t) (-1)) return -errno;
return result;
}
static int function_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi){
int result;
samba_fd fd;
DPRINTF(5, "(%s, %zd, fh=%llx, offset=%lld, flags=%o)\n", path, size,
(long long) fi->fh, (long long) offset, fi->flags);
if ((fd = function_get_fd(fi)) == NULL) return -EBADF;
result = samba_write(fd, offset, (char *) buf, size);
if (result == (ssize_t) (-1)) return -errno;
return result;
}
static int function_close(const char *path, struct fuse_file_info *fi){
samba_fd fd;
DPRINTF(5, "(%s, fh=%llx, flags=%o)\n", path, (long long) fi->fh, fi->flags);
if ((fd = function_get_fd(fi)) == NULL) return -EBADF;
if (samba_close(fd) != 0) return -errno;
return 0;
}
static int function_unlink(const char *path){
DPRINTF(5, "(%s)\n", path);
if (smbitem_what_is(path) != SMBITEM_SMB_SHARE_ITEM) return -EINVAL;
if (samba_unlink(path) != 0) return -errno;
return 0;
}
static int function_rename(const char *from, const char *to){
DPRINTF(5, "(%s, %s)\n", from, to);
if (smbitem_what_is(from) != SMBITEM_SMB_SHARE_ITEM) return -EINVAL;
if (smbitem_what_is(to) != SMBITEM_SMB_SHARE_ITEM) return -EINVAL;
if (samba_rename(from, to) != 0) return -errno;
return 0;
}
static int function_opendir(const char *path, struct fuse_file_info *fi){
samba_fd fd;
DPRINTF(5, "(%s, flags=%o, fh=%llx)\n", path, fi->flags, (long long) fi->fh);
switch(smbitem_what_is(path)){
case SMBITEM_SMBNETFS_DIR:
function_store_fd(fi, (samba_fd) (-1));
return 0;
case SMBITEM_SMB_NAME:
case SMBITEM_SMB_SHARE:
case SMBITEM_SMB_SHARE_ITEM:
if ((fd = samba_opendir(path)) == NULL) return -errno;
function_store_fd(fi, fd);
return 0;
default:
return -ENOTDIR;
}
}
static int function_closedir(const char *path, struct fuse_file_info *fi){
samba_fd fd;
DPRINTF(5, "(%s, fh=%llx, flags=%o)\n", path, (long long) fi->fh, fi->flags);
if ((fd = function_get_fd(fi)) == NULL) return -EBADF;
if (fd == (samba_fd) (-1)) return 0;
if (samba_closedir(fd) != 0) return -errno;
return 0;
}
static int function_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi){
samba_fd fd;
int error, rec_cnt;
struct stat st;
(void) offset;
DPRINTF(5, "(%s)\n", path);
memset(&st, 0, sizeof(st));
if ((fd = function_get_fd(fi)) == NULL) return -EBADF;
if (fd == (samba_fd) (-1)){
int i, show_hidden_hosts;
struct smbitem *dir;
while(*path == '/') path++;
if ((dir = smbitem_getdir(path)) == NULL) return -EBADF;
error = EINVAL;
st.st_mode = S_IFDIR;
if (filler(buf, ".", &st, 0)) goto error0;
if (filler(buf, "..", &st, 0)) goto error0;
show_hidden_hosts = function_get_hidden_hosts_visibility();
for(i = 0; i < dir->child_cnt; i++){
switch(dir->childs[i]->type){
case SMBITEM_GROUP:
st.st_mode = S_IFDIR;
break;
case SMBITEM_HOST:
st.st_mode = S_IFDIR;
if (!show_hidden_hosts && dir->childs[i]->is_hidden) continue;
break;
case SMBITEM_LINK:
st.st_mode = S_IFLNK;
break;
default:
goto error0;
}
if (filler(buf, dir->childs[i]->name, &st, 0)) goto end;
}
error = 0;
error0:
smbitem_release_dir(dir);
return -error;
}
rec_cnt = 0;
while(*path == '/') path++;
while(1){
int count;
char readdir_buf[4096];
char name[1024];
char link[1024];
struct smb_conn_dirent_rec *rec;
count = samba_readdir(fd, readdir_buf, sizeof(readdir_buf));
if (count == 0) break;
if (count < 0){
error = errno;
goto end;
}
rec = (struct smb_conn_dirent_rec *) readdir_buf;
for( ; count >= (int) sizeof(struct smb_conn_dirent_rec);
count -= sizeof(struct smb_conn_dirent_rec)){
if (strcmp(rec->d_name, "") == 0) goto next_record;
if (strcmp(rec->d_name, ".") == 0) goto next_record;
if (strcmp(rec->d_name, "..") == 0) goto next_record;
switch(rec->smbc_type){
case SMBC_WORKGROUP:
error = EBADF;
goto end;
case SMBC_SERVER:
st.st_mode = S_IFDIR;
snprintf(name, sizeof(name), "%s/%s", path, rec->d_name);
snprintf(link, sizeof(link), "../%s", rec->d_name);
smbitem_mkgroup(path, SMBITEM_SAMBA_TREE);
smbitem_mkhost(rec->d_name, path, 1, SMBITEM_SAMBA_TREE);
smbitem_mklink(name, link, SMBITEM_SAMBA_TREE);
break;
case SMBC_FILE_SHARE:
st.st_mode = S_IFDIR;
if ((rec->d_name[strlen(rec->d_name) - 1] == '$') &&
! function_get_dollar_share_visibility())
goto next_record;
break;
case SMBC_DIR:
st.st_mode = S_IFDIR;
break;
case SMBC_FILE:
st.st_mode = S_IFREG;
break;
default:
goto next_record;
}
if (rec_cnt == 0){
struct stat st;
error = EINVAL;
memset(&st, 0, sizeof(st));
st.st_mode = S_IFDIR;
if (filler(buf, ".", &st, 0)) goto end;
if (filler(buf, "..", &st, 0)) goto end;
rec_cnt += 2;
}
if (filler(buf, rec->d_name, &st, 0)){
error = EINVAL;
goto end;
}
rec_cnt++;
next_record:
rec++;
}
}
if (rec_cnt == 0){
error = EINVAL;
st.st_mode = S_IFDIR;
if (filler(buf, ".", &st, 0)) goto end;
if (filler(buf, "..", &st, 0)) goto end;
rec_cnt += 2;
}
error = 0;
end:
return -error;
}
static int function_mkdir(const char *path, mode_t mode){
DPRINTF(5, "(%s, %o)\n", path, mode);
if (smbitem_what_is(path) != SMBITEM_SMB_SHARE_ITEM) return -EINVAL;
if (samba_mkdir(path, mode) != 0) return -errno;
return 0;
}
static int function_rmdir(const char *path){
DPRINTF(5, "(%s)\n", path);
if (smbitem_what_is(path) != SMBITEM_SMB_SHARE_ITEM) return -EINVAL;
if (samba_rmdir(path) != 0) return -errno;
return 0;
}
static int function_stat(const char *path, struct stat *stbuf){
int i, count;
size_t len;
struct smbitem *dir;
char buf[2048];
DPRINTF(5, "(%s)\n", path);
if (stat_workaround_is_name_ignored(path)) return -ENOENT;
switch(smbitem_what_is(path)){
case SMBITEM_SMBNETFS_DIR:
while(*path == '/') path++;
count = 2;
if ((dir = smbitem_getdir(path)) == NULL) return -EINVAL;
for(i = 0; i < dir->child_cnt; i++)
switch(dir->childs[i]->type){
case SMBITEM_HOST:
case SMBITEM_GROUP:
count++;
break;
default:
break;
}
smbitem_release_dir(dir);
memset(stbuf, 0, sizeof(struct stat));
stbuf->st_mode = 0040777; /* protection */
stbuf->st_nlink = count; /* number of hard links */
stbuf->st_uid = 0; /* user ID of owner */
stbuf->st_gid = 0; /* group ID of owner */
stbuf->st_size = 0; /* total size, in bytes */
stbuf->st_blksize = 4096; /* blocksize for filesystem I/O */
return 0;
case SMBITEM_SMBNETFS_LINK:
DPRINTF(5, "link:(%s)\n", path);
if (smbitem_readlink(path, buf, sizeof(buf)) != 0) return -EINVAL;
len = strlen(buf);
memset(stbuf, 0, sizeof(struct stat));
stbuf->st_mode = 0120777; /* protection */
stbuf->st_nlink = 1; /* number of hard links */
stbuf->st_uid = 0; /* user ID of owner */
stbuf->st_gid = 0; /* group ID of owner */
stbuf->st_size = len; /* total size, in bytes */
stbuf->st_blksize = 4096; /* blocksize for filesystem I/O */
return 0;
case SMBITEM_SMB_NAME:
case SMBITEM_SMB_SHARE:
memset(stbuf, 0, sizeof(struct stat));
stbuf->st_mode = 0040777; /* protection */
stbuf->st_nlink = 2; /* number of hard links */
stbuf->st_uid = 0; /* user ID of owner */
stbuf->st_gid = 0; /* group ID of owner */
stbuf->st_size = 0; /* total size, in bytes */
stbuf->st_blksize = 4096; /* blocksize for filesystem I/O */
return 0;
case SMBITEM_SMB_SHARE_ITEM:
if (samba_stat(path, stbuf) != 0) return -errno;
if (S_ISREG(stbuf->st_mode) && function_get_noexec_attr())
stbuf->st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
return 0;
default:
return -EINVAL;
}
}
static int function_fstat(const char *path, struct stat *stbuf,
struct fuse_file_info *fi){
int i, count;
struct smbitem *dir;
samba_fd fd;
(void) path;
DPRINTF(5, "(%s)\n", path);
if ((fd = function_get_fd(fi)) == NULL) return -EBADF;
if (fd == (samba_fd) (-1)){
/* SMBNETFS_DIR */
while(*path == '/') path++;
count = 2;
if ((dir = smbitem_getdir(path)) == NULL) return -EBADF;
for(i = 0; i < dir->child_cnt; i++)
switch(dir->childs[i]->type){
case SMBITEM_HOST:
case SMBITEM_GROUP:
count++;
break;
default:
break;
}
smbitem_release_dir(dir);
memset(stbuf, 0, sizeof(struct stat));
stbuf->st_mode = 0040777; /* protection */
stbuf->st_nlink = count; /* number of hard links */
stbuf->st_uid = 0; /* user ID of owner */
stbuf->st_gid = 0; /* group ID of owner */
stbuf->st_size = 0; /* total size, in bytes */
stbuf->st_blksize = 4096; /* blocksize for filesystem I/O */
return 0;
}
if (samba_fstat(fd, stbuf) != 0) return -errno;
if (S_ISREG(stbuf->st_mode) && function_get_noexec_attr())
stbuf->st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
return 0;
}
static int function_ftruncate(const char *path, off_t size,
struct fuse_file_info *fi){
samba_fd fd;
DPRINTF(5, "(%s, %lld)\n", path, (long long) size);
if (size < 0) return -EINVAL;
if ((fd = function_get_fd(fi)) == NULL) return -EBADF;
if (samba_ftruncate(fd, size) != 0) return -errno;
return 0;
}
static int function_chmod(const char *path, mode_t mode){
DPRINTF(5, "(%s, %o)\n", path, mode);
if (smbitem_what_is(path) != SMBITEM_SMB_SHARE_ITEM) return -EINVAL;
if (samba_chmod(path, mode) != 0) return -errno;
return 0;
}
static int function_utimes(const char *path, struct utimbuf *buffer){
struct timeval tbuf[2];
DPRINTF(5, "(%s, %u)\n", path, (unsigned int)buffer->modtime);
if (smbitem_what_is(path) != SMBITEM_SMB_SHARE_ITEM) return -EINVAL;
tbuf[0].tv_sec = buffer->actime;
tbuf[0].tv_usec = 0;
tbuf[1].tv_sec = buffer->modtime;
tbuf[1].tv_usec = 0;
if (samba_utimes(path, tbuf) != 0) return -errno;
return 0;
}
/* libfuse does not support lsetxattr() and fsetxattr(), but samba does */
static int function_setxattr(const char *path, const char *name,
const char *value, size_t size, int flags){
DPRINTF(5, "(%s, name=%s, value=%s, size=%zd, flags=%o)\n", path,
name, value, size, flags);
if (smbitem_what_is(path) != SMBITEM_SMB_SHARE_ITEM) return -ENOTSUP;
if (!function_check_xattr_name(name)) return -ENOTSUP;
if (samba_setxattr(path, name, value, size, flags) != 0) return -errno;
return 0;
}
/* libfuse does not support lgetxattr() and fgetxattr(), but samba does */
static int function_getxattr(const char *path, const char *name,
char *value, size_t size){
DPRINTF(5, "(%s, name=%s, size=%zd)\n", path, name, size);
if (smbitem_what_is(path) != SMBITEM_SMB_SHARE_ITEM) return -ENOTSUP;
if (!function_check_xattr_name(name)) return -ENOTSUP;
if (samba_getxattr(path, name, value, size) != 0) return -errno;
return 0;
}
/* libfuse does not support llistxattr() and flistxattr(), but samba does */
static int function_listxattr(const char *path, char *list, size_t size){
DPRINTF(5, "(%s, size=%zd)\n", path, size);
if (smbitem_what_is(path) != SMBITEM_SMB_SHARE_ITEM) return -ENOTSUP;
if (samba_listxattr(path, list, size) != 0) return -errno;
return 0;
}
/* libfuse does not support lremovexattr() and fremovexattr(), but samba does */
static int function_removexattr(const char *path, const char *name){
DPRINTF(5, "(%s, name=%s)\n", path, name);
if (smbitem_what_is(path) != SMBITEM_SMB_SHARE_ITEM) return -ENOTSUP;
if (!function_check_xattr_name(name)) return -ENOTSUP;
if (samba_removexattr(path, name) != 0) return -errno;
return 0;
}
static int function_readlink(const char *path, char *buf, size_t size){
DPRINTF(5, "(%s, %zd)\n", path, size);
if (smbitem_what_is(path) != SMBITEM_SMBNETFS_LINK) return -EINVAL;
if (smbitem_readlink(path, buf, size) != 0) return -EINVAL;
return 0;
}
static int function_mknod(const char *path, mode_t mode, dev_t rdev){
samba_fd fd;
(void) rdev;
DPRINTF(5, "(%s, %o)\n", path, mode);
if ((mode & S_IFMT) != S_IFREG) return -EPERM;
if (smbitem_what_is(path) != SMBITEM_SMB_SHARE_ITEM) return -EINVAL;
if ((fd = samba_creat(path, mode)) == NULL) return -errno;
if (samba_close(fd) != 0) return -errno;
return 0;
}
static int function_chown(const char *path, uid_t uid, gid_t gid){
(void) path;
(void) uid;
(void) gid;
DPRINTF(5, "(%s, uid=%d, gid=%d)\n", path, uid, gid);
if (function_get_quiet_flag()) return 0;
else return -EPERM;
}
static int function_truncate(const char *path, off_t size){
samba_fd fd;
DPRINTF(5, "(%s, %lld)\n", path, (long long) size);
if (size < 0) return -EINVAL;
if (smbitem_what_is(path) != SMBITEM_SMB_SHARE_ITEM) return -EINVAL;
if ((fd = samba_open(path, O_RDWR, 0777)) == NULL) return -errno;
if (samba_ftruncate(fd, size) != 0){
int error = errno;
samba_close(fd);
return -error;
}
if (samba_close(fd) != 0) return -errno;
return 0;
}
static int function_statfs(const char *path, struct statvfs *stbuf){
int free_space_blocks;
DPRINTF(5, "(%s)\n", path);
free_space_blocks = function_get_free_space_size();
memset(stbuf, 0, sizeof(struct statvfs));
stbuf->f_bsize = 4096;
stbuf->f_frsize = 4096;
if (free_space_blocks > 0){
stbuf->f_blocks = free_space_blocks;
stbuf->f_bfree = free_space_blocks;
stbuf->f_bavail = free_space_blocks;
stbuf->f_ffree = 32768;
stbuf->f_favail = 32768;
}
stbuf->f_namemax = FILENAME_MAX;
return 0;
}
struct fuse_operations smb_oper = {
.open = function_open,
.read = function_read,
.write = function_write,
.release = function_close,
.unlink = function_unlink,
.rename = function_rename,
.opendir = function_opendir,
.releasedir = function_closedir,
.readdir = function_readdir,
.mkdir = function_mkdir,
.rmdir = function_rmdir,
.getattr = function_stat,
.fgetattr = function_fstat,
.ftruncate = function_ftruncate,
.chmod = function_chmod,
.utime = function_utimes,
.setxattr = function_setxattr,
.getxattr = function_getxattr,
.listxattr = function_listxattr,
.removexattr= function_removexattr,
.readlink = function_readlink,
.mknod = function_mknod,
.chown = function_chown,
.truncate = function_truncate,
.statfs = function_statfs,
// .init = function_init, /* event.c */
// .destroy = function_destroy, /* event.c */
// .fsyncdir = function_fsyncdir,
// .access = function_access,
};