Example #1
0
/*
 * FUSE calls its equivalent of stat(2) "getattr".  This just gets stat
 * information, e.g. file size and permissions.
 */
static int brp_getattr(const char *in_path, struct stat *stbuf)
{
	SET_CALLER_UID();

	struct out_item *out_item;
	struct in_item *in_item;
	char *tail;
	char *config_str;
	int ret, fd;
	int stratum_id;

	if (in_path[0] == '/' && in_path[1] == '\0') {
		memcpy(stbuf, &parent_stat, sizeof(parent_stat));
		return 0;
	}

	if (strcmp(in_path, "/reparse_config") == 0) {
		memcpy(stbuf, &reparse_stat, sizeof(reparse_stat));
		config_str = config_contents();
		if (config_str) {
			stbuf->st_size = strlen(config_str);
			free(config_str);
			return 0;
		} else {
			return -ENOMEM;
		}
	}

	if ( (ret = corresponding((char*)in_path, &fd, stbuf, &out_item, &stratum_id, &in_item, &tail)) >= 0) {
		stat_filter(stbuf, fd, out_item->filter, stratum_id, in_item, tail);
		return 0;
	} else {
		return ret;
	}
}
Example #2
0
/*
 * Read file contents.
 */
static int brp_read(const char *in_path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
{
	(void)fi;
	SET_CALLER_UID();

	struct out_item *out_item;
	int stratum_id;
	struct in_item *in_item;
	char *tail;
	char *config_str;
	struct stat stbuf;
	int ret, fd;

	if (strcmp(in_path, "/reparse_config") == 0) {
		config_str = config_contents();
		if (!config_str) {
			return -ENOMEM;
		}
		ret = MIN(strlen(config_str + offset), size);
		memcpy(buf, config_str + offset, ret);
		free(config_str);
		return ret;
	}

	ret = corresponding((char*) in_path, &fd, &stbuf, &out_item, &stratum_id, &in_item, &tail);
	if (ret < 0) {
		return ret;
	}

	return read_filter(fd, out_item->filter, stratum_id, in_item, tail, buf, size, offset);
}
Example #3
0
static int bru_truncate(const char *path, off_t length){
	SET_CALLER_UID();
	REDIR_PATH(path, new_path);

	int ret = truncate(new_path, length);

	SET_RET_ERRNO();
	return ret;
}
Example #4
0
static int bru_fgetattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
{
	SET_CALLER_UID();

	int ret = fstat(fi->fh, stbuf);

	SET_RET_ERRNO();
	return ret;
}
Example #5
0
static int bru_ftruncate(const char *path, off_t length, struct fuse_file_info *fi)
{
	SET_CALLER_UID();

	int ret = ftruncate(fi->fh, length);

	SET_RET_ERRNO();
	return ret;
}
Example #6
0
/*
 * FUSE uses the word "release" rather than "close".
 */
static int bru_release(const char *path, struct fuse_file_info *fi)
{
	SET_CALLER_UID();

	int ret = close(fi->fh);

	SET_RET_ERRNO();
	return ret;
}
Example #7
0
static int bru_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
{
	SET_CALLER_UID();

	int ret = pwrite(fi->fh, buf, size, offset);

	SET_RET_ERRNO();
	return ret;
}
Example #8
0
static int bru_chown(const char *path, uid_t owner, gid_t group){
	SET_CALLER_UID();
	REDIR_PATH(path, new_path);
	
	int ret = lchown(new_path, owner, group);

	SET_RET_ERRNO();
	return ret;
}
Example #9
0
static int bru_chmod(const char *path, mode_t mode){
	SET_CALLER_UID();
	REDIR_PATH(path, new_path);
	
	int ret = chmod(new_path, mode);

	SET_RET_ERRNO();
	return ret;
}
Example #10
0
static int bru_removexattr(const char *path, const char *name)
{
	SET_CALLER_UID();
	REDIR_PATH(path, new_path);

	int ret = lremovexattr(new_path, name);

	SET_RET_ERRNO();
	return ret;
}
Example #11
0
static int bru_mknod(const char *path, mode_t mode, dev_t dev)
{
	SET_CALLER_UID();
	REDIR_PATH(path, new_path);

	int ret = mknod(new_path, mode, dev);

	SET_RET_ERRNO();
	return ret;
}
Example #12
0
static int bru_listxattr(const char *path, char *list, size_t size)
{
	SET_CALLER_UID();
	REDIR_PATH(path, new_path);

	int ret = llistxattr(new_path, list, size);

	SET_RET_ERRNO();
	return ret;
}
Example #13
0
static int bru_getxattr(const char *path, const char *name, char *value, size_t size)
{
	SET_CALLER_UID();
	REDIR_PATH(path, new_path);

	int ret = lgetxattr(new_path, name, value, size);

	SET_RET_ERRNO();
	return ret;
}
Example #14
0
/*
 * Using statvfs instead of statfs, per FUSE API.
 */
static int bru_statfs(const char *path, struct statvfs *buf)
{
	SET_CALLER_UID();
	REDIR_PATH(path, new_path);
	
	int ret = statvfs(new_path, buf);

	SET_RET_ERRNO();
	return ret;
}
Example #15
0
static int bru_rmdir(const char *path)
{
	SET_CALLER_UID();
	REDIR_PATH(path, new_path);

	int ret = rmdir(new_path);

	SET_RET_ERRNO();
	return ret;
}
Example #16
0
static int bru_link(const char *old_path, const char *new_path){
	SET_CALLER_UID();
	REDIR_PATH(old_path, redir_old_path);
	REDIR_PATH(new_path, redir_new_path);

	int ret = link(redir_old_path, redir_new_path);

	SET_RET_ERRNO();
	return ret;
}
Example #17
0
static int bru_symlink(const char *symlink_string, const char *path)
{
	SET_CALLER_UID();
	REDIR_PATH(path, new_path);

	int ret = symlink(symlink_string, new_path);

	SET_RET_ERRNO();
	return ret;
}
Example #18
0
static int bru_getattr(const char *path, struct stat *stbuf)
{
	SET_CALLER_UID();
	REDIR_PATH(path, new_path);

	int ret = lstat(new_path, stbuf);

	SET_RET_ERRNO();
	return ret;
}
Example #19
0
/*
 * This is typically used to indicate a file should be shortened.  Like
 * write(), it is only being used here as an indication to reload the
 * configuration and stratum information.
 */
static int brp_truncate(const char *in_path, off_t length)
{
	SET_CALLER_UID();

	if (write_attempt(in_path) == 0) {
		return 0;
	} else {
		return -EACCES;
	}
}
Example #20
0
/*
 * TODO: To simplify things, we're mandating absolute paths.  We should
 * probably properly handle relative paths for this later and remove this
 * restriction.  Given this, the first argument to utimensat() is ignored.
 */
static int bru_utimens(const char *path, const struct timespec *times)
{
	SET_CALLER_UID();
	REDIR_PATH(path, new_path);

	int ret = utimensat(0, new_path, times, AT_SYMLINK_NOFOLLOW);

	SET_RET_ERRNO();
	return ret;
}
Example #21
0
/*
 * Yes, FUSE uses creat*e* and POSIX uses creat.
 */
static int bru_create(const char *path, mode_t mode, struct fuse_file_info *fi)
{
	SET_CALLER_UID();
	REDIR_PATH(path, new_path);

	int ret = 0;
	if ((fi->fh = creat(new_path, mode)) < 0)
		ret = -errno;

	return ret;
}
Example #22
0
/*
 * FUSE uses the word "release" rather than "close".
 */
static int bru_releasedir(const char *path, struct fuse_file_info *fi)
{
	SET_CALLER_UID();

	/*
	 * FUSE provides an "uint64_t" rather than DIR*
	 */
	int ret = closedir((DIR *) fi->fh);

	SET_RET_ERRNO();
	return ret;
}
Example #23
0
/*
 * This is typically used to write to a file, just as you'd expect from the
 * name.  However, for this filesystem, we only use it as an indication to
 * reload the configuration and stratum information.
 */
static int brp_write(const char *in_path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
{
	(void)size;
	(void)offset;
	(void)fi;
	SET_CALLER_UID();

	if (write_attempt(in_path) == 0) {
		return strlen(buf);
	} else {
		return -EACCES;
	}
}
Example #24
0
/*
 * There is no POSIX fsyncdir - presumably this is just fsync when called on a
 * directory.  Mimicking code from (non-dir) fsync.
 */
static int bru_fsyncdir(const char *path, int datasync, struct fuse_file_info *fi)
{
	SET_CALLER_UID();

	int ret;
	if(datasync)
		ret = fdatasync(fi->fh);
	else
		ret = fsync(fi->fh);

	SET_RET_ERRNO();
	return ret;
}
Example #25
0
/*
 * We cannot use POSIX access() for two reasons:
 * 1. It uses real uid, rather than effective or filesystem uid.
 * 2. It dereferences symlinks.
 * Instead, we're using faccessat().
 * TODO: To simplify things, we're mandating absolute paths.  We should
 * probably properly handle relative paths for this later and remove this
 * restriction.  Given this, the first argument to faccessat() is ignored.
 * TODO: POSIX faccessat() doesn't support AT_SYMLINK_NOFOLLOW, and neither
 * does musl.  See if we can upstream support into musl.  Utilizing
 * AT_SYMLINK_NOFOLLOW is disabled for now so it will compile against musl.
 */
static int bru_access(const char *path, int mask)
{
	SET_CALLER_UID();
	REDIR_PATH(path, new_path);

	/*
	 * Disabling AT_SYMLINK_NOFOLLOW since musl does not (yet?) support it.
	 * int ret = faccessat(0, new_path, mask, AT_EACCESS | AT_SYMLINK_NOFOLLOW);
	 */
    int ret = faccessat(0, new_path, mask, AT_EACCESS);

	SET_RET_ERRNO();
	return ret;
}
Example #26
0
/*
 * Unlike POSIX open(), it seems the return value should be 0 for success, not
 * the file descriptor.
 */
static int bru_open(const char *path, struct fuse_file_info *fi)
{
	SET_CALLER_UID();
	REDIR_PATH(path, new_path);

	int ret = open(new_path, fi->flags);

	if (ret < 0) {
		ret = -errno;
	} else {
		fi->fh = ret;
		ret = 0;
	}

	return ret;
}
Example #27
0
/*
 * FUSE uses this primarily for a permissions check.  Actually returning a
 * file handler is optional.  Unlike POSIX, this does not directly return
 * the file handler, but rather returns indictation of whether or not the user
 * may use opendir().
 */
static int bru_opendir(const char *path, struct fuse_file_info *fi)
{
	SET_CALLER_UID();
	REDIR_PATH(path, new_path);

	int ret;
	DIR *d = opendir(new_path);
	/*
	 * It seems FUSE wants an int pointer, not a directory stream pointer.
	 */
	fi->fh = (intptr_t) d;
	if (d)
		ret = 0;
	else
		ret = -errno;

	return ret;
}
Example #28
0
static int bru_readlink(const char *path, char *buf, size_t bufsize)
{
	SET_CALLER_UID();
	REDIR_PATH(path, new_path);

	/*
	 * Alternative approach zero out out the buffer:
	 * memset(buf, '\0', bufsize);
	 * TODO: Benchmark if this is faster.
	 */
	int bytes_read = readlink(new_path, buf, bufsize);
	int ret = 0;
	if(bytes_read < 0)
		ret = -errno;
	else if(bytes_read <= bufsize)
		buf[bytes_read] = '\0';

	return ret;
}
Example #29
0
/*
 * Check if user has permissions to do something with file. e.g. read or write.
 */
static int brp_open(const char *in_path, struct fuse_file_info *fi)
{
	SET_CALLER_UID();

	struct out_item *out_item;
	int stratum_id;
	struct in_item *in_item;
	char *tail;
	int ret;
	struct stat stbuf;

	/*
	 * /reparse_config is the only file which could possibly be written to.
	 * Get that out of the way here so we can assume everything else later is
	 * only being read.
	 */
	if (strcmp(in_path, "/reparse_config") == 0) {
		struct fuse_context *context = fuse_get_context();
		if (context->uid != 0) {
			/* Non-root users cannot do anything with this file. */
			return -EACCES;
		} else {
			return 0;
		}
	}

	/*
	 * Everything else in this filesystem is read-only.  If the user requested
	 * anything else, return EACCES.
	 *
	 * Note the way permissions are stored in fi->flags do *not* have a single
	 * bit flag for read or write, hence the unusual looking check below.  See
	 * `man 2 open`.
	 */
	if ((fi->flags & 3) != O_RDONLY ) {
		return -EACCES;
	}

	if ( (ret = corresponding((char*)in_path, NULL, &stbuf, &out_item, &stratum_id, &in_item, &tail)) >= 0) {
		return 0;
	}
	return -ENOENT;
}
Example #30
0
/*
 * Provides contents of a directory, e.g. as used by `ls`.
 */
static int brp_readdir(const char *in_path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi)
{
	SET_CALLER_UID();

	(void) offset;
	(void) fi;

	char out_path[PATH_MAX+1];
	size_t i, j;
	int st;
	size_t in_path_len = strlen(in_path);
	/* handle root directory specially */
	if (in_path_len == 1) {
		in_path_len = 0;
	}
	struct stat stbuf;
	struct in_item *in_item;
	int ret_val = -ENOENT;
	char *slash;
	int *stratum_root_fd = malloc(sizeof(int)*nstratum);

	int ret = chdir(STRATA_ROOT);
	if (unlikely(ret < 0)) {
		// strata root missing?!
		perror("Failed to chdir to strata root");
		exit(1);
	}

	for (st = 0; st < nstratum; st++)
		stratum_root_fd[st] = open(stratum[st], O_RDONLY|O_DIRECTORY);

	struct dirent *dir;

	struct str_vec v;
	str_vec_new(&v);

	for (st = 0; st < nstratum; st++) {
		if (unlikely(stratum_root_fd[st] < 0))
			continue;

		ret = seteuid(0);
		if (unlikely(ret < 0))
			return -errno;

		ret = fchdir(stratum_root_fd[st]);
		if (unlikely(ret < 0))
			continue;
		ret = chroot(".");
		if (unlikely(ret < 0))
			continue;

		SET_CALLER_UID();
		for (i = 0; i < out_item_count; i++) {
			/*
			 * Check for contents of one of the configured directories
			 */
			in_item = out_items[i].in_items;
			if (strncmp(in_path, out_items[i].path, out_items[i].path_len) ||
			    (in_path[out_items[i].path_len] != '\0' &&
			     in_path[out_items[i].path_len]  != '/') ||
			    out_items[i].file_type != FILE_TYPE_DIRECTORY)
				continue;
			for (j = 0; j < out_items[i].in_item_count; j++) {
				if (in_item[j].stratum_id >= 0 && in_item[j].stratum_id != st)
					continue;

				if (in_item[j].path_len+
				    in_path_len-out_items[i].path_len > PATH_MAX)
					continue;

				strcpy(out_path, in_item[j].path);
				strcat(out_path, in_path+out_items[i].path_len);

				ret = stat(out_path, &stbuf);
				if (ret < 0)
					continue;

				if (S_ISDIR(stbuf.st_mode)) {
					DIR *d = opendir(out_path);
					if (!d) {
						perror("opendir()");
						continue;
					}
					while ( (dir = readdir(d)) ) {
						str_vec_append(&v, dir->d_name);
					}
					closedir(d);
				} else {
					if (strrchr(out_path, '/')) {
						str_vec_append(&v, strrchr(out_path, '/')+1);
					} else {
						str_vec_append(&v, out_path);
					}
				}
			}
		}
		for (i = 0; i < out_item_count; i++) {
			/*
			 * Check for a match directly on one of the configured items or a virtual parent directory
			 */
			if (strncmp(out_items[i].path, in_path, in_path_len) ||
			    out_items[i].path[in_path_len] != '/')
				continue;

			in_item = out_items[i].in_items;
			for (j = 0; j < out_items[i].in_item_count; j++) {
				if (in_item[j].stratum_id >= 0 && in_item[j].stratum_id != st)
					continue;

				ret = stat(in_item[j].path, &stbuf);
				if (ret < 0)
					continue;
				if (out_items[i].path_len-in_path_len-1 > PATH_MAX)
					continue;
				strcpy(out_path, out_items[i].path+in_path_len+1);
				if ( (slash = strchr(out_path, '/')) ) {
					*slash = '\0';
				}
				str_vec_append(&v, out_path);
				break;
			}
		}
	}

	/*
	 * Handle reparse_config on root
	 */
	if (in_path[0] == '/' && in_path[1] == '\0') {
		str_vec_append(&v, "reparse_config");
	}

	str_vec_uniq(&v);
	for (i = 0; i < v.len; i++) {
		if (v.array[i][0] != '\0') {
			filler(buf, v.array[i], NULL, 0);
			ret_val = 0;
		}
	}

	str_vec_free(&v);

	for (st = 0; st < nstratum; st++)
		if (stratum_root_fd[st] >= 0)
			close(stratum_root_fd[st]);

	free(stratum_root_fd);

	// If anything goes wrong here, we are stuck in a different root.
	// In that case we give up and quit.
	ret = seteuid(0);
	if (unlikely(ret < 0)) {
		perror("seteuid");
		exit(1);
	}
	ret = fchdir(brp_orig_root);
	if (unlikely(ret < 0)) {
		perror("Failed to chdir to original root");
		exit(1);
	}

	ret = chroot(".");
	if (unlikely(ret < 0)) {
		perror("Failed to chroot to original root");
		exit(1);
	}

	ret = fchdir(brp_orig_cwd);
	if (unlikely(ret < 0)) {
		perror("Failed to change back to old cwd");
		exit(1);
	}

	return ret_val;
}