From 17496a39b1c6f6c703219d559254e894a40d0117 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Wed, 10 Jul 2019 16:03:17 +0200 Subject: [PATCH] s3:net: add 'vfs' hierarchy with 'stream2adouble' command This adds a new top-level command hierarchy 'vfs' that can be used to add commands that access the smbd VFS stack. The first command to be implemented is 'stream2adouble' which can be used to convert stream metadata to AppleDouble files. Signed-off-by: Ralph Boehme Reviewed-by: Jeremy Allison --- docs-xml/manpages/net.8.xml | 66 +++++++ source3/utils/net.c | 27 +++ source3/utils/net.h | 3 + source3/utils/net_proto.h | 2 + source3/utils/net_vfs.c | 339 ++++++++++++++++++++++++++++++++++++ source3/utils/wscript_build | 2 + 6 files changed, 439 insertions(+) create mode 100644 source3/utils/net_vfs.c diff --git a/docs-xml/manpages/net.8.xml b/docs-xml/manpages/net.8.xml index df423a3df06..d7fb1e15b0f 100644 --- a/docs-xml/manpages/net.8.xml +++ b/docs-xml/manpages/net.8.xml @@ -392,6 +392,26 @@ + + + + --recursive + Traverse a directory + hierarchy. + + + + --continue + Continue traversing a directory hierarchy in + case conversion of one file fails. + + + + --follow-symlinks + Follow symlinks encountered while traversing a + directory. + + &stdarg.encrypt; &popt.common.samba.client; @@ -2889,6 +2909,52 @@ Dump the locking table of a certain global lock. + + vfs + Access shared filesystem through the VFS. + + + vfs stream2abouble [--recursive] [--verbose] [--continue] [--follow-symlinks] <replaceable>share</replaceable> <replaceable>path</replaceable> + + Convert file streams to AppleDouble files. + + + share + A Samba share. + + + + + path A relative path of something in + the Samba share. "." can be used for the root directory of the + share. + + + + Options: + + + --recursive + Traverse a directory hierarchy. + + + --verbose + Verbose output. + + + --continue + Continue traversing a directory hierarchy if a single + conversion fails. + + + --follow-symlinks + Follow symlinks encountered while traversing a + directory. + + + + + HELP [COMMAND] diff --git a/source3/utils/net.c b/source3/utils/net.c index 7c5b6a05be7..71b9b07d7d7 100644 --- a/source3/utils/net.c +++ b/source3/utils/net.c @@ -892,6 +892,14 @@ static struct functable net_func[] = { "'net tdb' commands.") }, + { "vfs", + net_vfs, + NET_TRANSPORT_LOCAL, + N_("Filesystem operation through the VFS stack"), + N_(" Use 'net help vfs' to get more information about " + "'net vfs' commands.") + }, + #ifdef WITH_FAKE_KASERVER { "afs", net_afs, @@ -1257,6 +1265,25 @@ static void get_credentials_file(struct net_context *c, .argInfo = POPT_ARG_NONE, .arg = &c->opt_json, }, + /* Options for 'net vfs' */ + { + .longName = "continue", + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_continue_on_error, + .descrip = "Continue on errors", + }, + { + .longName = "recursive", + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_recursive, + .descrip = "Traverse directory hierarchy", + }, + { + .longName = "follow-symlinks", + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_follow_symlink, + .descrip = "follow symlinks", + }, POPT_COMMON_SAMBA POPT_TABLEEND }; diff --git a/source3/utils/net.h b/source3/utils/net.h index 0d01ad45010..573b6e80d29 100644 --- a/source3/utils/net.h +++ b/source3/utils/net.h @@ -87,6 +87,9 @@ struct net_context { int opt_no_dns_updates; int opt_keep_account; int opt_json; + int opt_continue_on_error; + int opt_recursive; + int opt_follow_symlink; int opt_have_ip; struct sockaddr_storage opt_dest_ip; diff --git a/source3/utils/net_proto.h b/source3/utils/net_proto.h index 22fe39e0f1c..13058be8b8b 100644 --- a/source3/utils/net_proto.h +++ b/source3/utils/net_proto.h @@ -457,4 +457,6 @@ int net_notify(struct net_context *c, int argc, const char **argv); int net_tdb(struct net_context *c, int argc, const char **argv); +int net_vfs(struct net_context *c, int argc, const char **argv); + #endif /* _NET_PROTO_H_ */ diff --git a/source3/utils/net_vfs.c b/source3/utils/net_vfs.c new file mode 100644 index 00000000000..041f98f7a82 --- /dev/null +++ b/source3/utils/net_vfs.c @@ -0,0 +1,339 @@ +/* + * Samba Unix/Linux SMB client library + * Distributed SMB/CIFS Server Management Utility + * Copyright (C) 2019 Ralph Boehme + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "includes.h" +#include +#include +#include +#include "system/filesys.h" +#include "system/passwd.h" +#include "popt_common.h" +#include "lib/param/loadparm.h" +#include "lib/param/param.h" +#include "libcli/security/security.h" +#include "smbd/proto.h" +#include "locking/proto.h" +#include "auth.h" +#include "lib/adouble.h" +#include "lib/string_replace.h" +#include "utils/net.h" + +#define NET_VFS_CMD_STREAM_TO_ADOUBLE "stream2adouble" + +static struct net_vfs_state { + TALLOC_CTX *mem_ctx; + struct net_context *c; + struct auth_session_info *session_info; + struct conn_struct_tos *conn_tos; +} state; + +static void net_vfs_usage(void) +{ + fprintf(stderr, + "Usage:\n" + "net vfs [OPTIONS] ....\n"); +} + +static void net_vfs_stream_to_appledouble_usage(void) +{ + fprintf(stderr, + "Usage:\n" + "net vfs " NET_VFS_CMD_STREAM_TO_ADOUBLE + " [OPTIONS] [ ...]\n" + "Options:\n" + " --verbose verbose output\n" + " --continue continue on error\n" + " --recursive traverse directory hierarchy\n" + " --follow-symlinks follow symlinks\n"); +} + +static bool net_vfs_make_session_info(struct auth_session_info **session_info) +{ + NTSTATUS status; + + if (non_root_mode()) { + struct passwd *p = NULL; + + p = getpwuid(geteuid()); + if (p == NULL) { + fprintf(stderr, "getpwuid(%d) failed\n", geteuid()); + return false; + } + + status = make_session_info_from_username(state.mem_ctx, + p->pw_name, + false, + session_info); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "session_info from username failed\n"); + return false; + } + + return true; + } + + status = init_system_session_info(state.mem_ctx); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "init_system_session_info failed\n"); + return false; + } + + status = make_session_info_system(state.mem_ctx, session_info); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "make_session_info_system failed\n"); + return false; + } + + return true; +} + +static int net_vfs_init(struct net_context *c, int argc, const char **argv) +{ + const char *service = NULL; + char *share_root = NULL; + int snum; + NTSTATUS status; + bool ok; + int rc = 1; + + state = (struct net_vfs_state) { + .c = c, + .mem_ctx = c, + }; + + if (argc < 1) { + net_vfs_usage(); + goto done; + } + + if (geteuid() != 0 && !uid_wrapper_enabled()) { + fprintf(stderr, "'net vfs' must be run as root.\n"); + goto done; + } + + smb_init_locale(); + umask(0); + sec_init(); + setup_logging("net", DEBUG_STDOUT); + lp_set_cmdline("log level", "0"); + + ok = lp_load_with_shares(get_dyn_CONFIGFILE()); + if (!ok) { + fprintf(stderr, "lp_load_with_shares failed\n"); + goto done; + } + + ok = locking_init(); + if (!ok) { + fprintf(stderr, "locking init failed\n"); + goto done; + } + + ok = net_vfs_make_session_info(&state.session_info); + if (!ok) { + goto done; + } + + service = argv[0]; + snum = lp_servicenumber(service); + if (snum == -1) { + fprintf(stderr, "unknown service: %s\n", service); + goto done; + } + + share_root = lp_path(state.mem_ctx, snum); + if (share_root == NULL) { + fprintf(stderr, "Failed to find share root for service: %s\n", + service); + goto done; + } + + status = create_conn_struct_tos_cwd(global_messaging_context(), + snum, + share_root, + state.session_info, + &state.conn_tos); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + state.conn_tos->conn->share_access = FILE_GENERIC_ALL; + state.conn_tos->conn->read_only = false; + file_init(state.conn_tos->conn->sconn); + + ok = become_user_by_session(state.conn_tos->conn, state.session_info); + if (!ok) { + fprintf(stderr, "become_user_by_session failed\n"); + goto done; + } + + rc = 0; +done: + return rc; +} + +static bool do_unfruit(const char *path) +{ + struct smb_filename *smb_fname = NULL; + char *p = NULL; + bool converted; + int ret; + bool ok; + + p = strrchr_m(path, '/'); + if (p != NULL) { + if (p[1] == '.' && p[2] == '_') { + return true; + } + } + + smb_fname = synthetic_smb_fname(state.mem_ctx, + path, + NULL, + NULL, + 0); + if (smb_fname == NULL) { + return false; + } + + ret = SMB_VFS_STAT(state.conn_tos->conn, smb_fname); + if (ret != 0) { + fprintf(stderr, "%s: %s\n", path, strerror(errno)); + if (state.c->opt_continue_on_error) { + return true; + } + return false; + } + + ok = ad_unconvert(state.mem_ctx, + state.conn_tos->conn->vfs_handles, + macos_string_replace_map, + smb_fname, + &converted); + if (!ok) { + fprintf(stderr, "Converting failed: %s\n", path); + if (state.c->opt_continue_on_error) { + return true; + } + return false; + } + + if (converted) { + fprintf(stdout, "Converted: %s\n", path); + } else if (state.c->opt_verbose) { + fprintf(stdout, "%s\n", path); + } + return true; +} + +static int nftw_cb(const char *path, + const struct stat *sb, + int typeflag, + struct FTW *ftwbuf) +{ + bool ok; + + if (typeflag == FTW_SL) { + if (state.c->opt_verbose) { + fprintf(stdout, "Ignoring symlink: %s\n", path); + } + return 0; + } + + ok = do_unfruit(path); + if (!ok) { + return -1; + } + + return 0; +} + +static int net_vfs_stream_to_appledouble(struct net_context *net, + int argc, + const char **argv) +{ + int i; + int ret; + bool ok; + int rc = 1; + + if (argc < 2 || net->display_usage) { + net_vfs_stream_to_appledouble_usage(); + goto done; + } + + ret = net_vfs_init(net, argc, argv); + if (ret != 0) { + goto done; + } + + for (i = 1; i < argc; i++) { + const char *path = argv[i]; + + if (path[0] == '/') { + fprintf(stderr, "ignoring absolute path: %s\n", path); + if (state.c->opt_continue_on_error) { + continue; + } + goto done; + } + + if (!state.c->opt_recursive) { + ok = do_unfruit(path); + if (!ok) { + if (!state.c->opt_continue_on_error) { + goto done; + } + } + continue; + } + + ret = nftw(path, + nftw_cb, + 256, + state.c->opt_follow_symlink ? 0 : FTW_PHYS); + if (ret != 0) { + fprintf(stderr, "%s: %s\n", path, strerror(errno)); + if (!state.c->opt_continue_on_error) { + goto done; + } + } + } + + rc = 0; + +done: + return rc; +} + +static struct functable func[] = { + { + NET_VFS_CMD_STREAM_TO_ADOUBLE, + net_vfs_stream_to_appledouble, + NET_TRANSPORT_LOCAL, + N_("Convert streams to AppleDouble files"), + N_("net vfs " NET_VFS_CMD_STREAM_TO_ADOUBLE " [OPTIONS] [ ...]") + }, + {NULL, NULL, 0, NULL, NULL} +}; + +int net_vfs(struct net_context *c, int argc, const char **argv) +{ + return net_run_function(c, argc, argv, "net vfs", func); +} diff --git a/source3/utils/wscript_build b/source3/utils/wscript_build index 9d9aa56bf37..8393ab92b88 100644 --- a/source3/utils/wscript_build +++ b/source3/utils/wscript_build @@ -224,6 +224,7 @@ bld.SAMBA3_BINARY('net', net_afs.c net_notify.c net_tdb.c + net_vfs.c ../registry/reg_parse.c ../registry/reg_format.c ../registry/reg_import.c @@ -269,6 +270,7 @@ bld.SAMBA3_BINARY('net', CONN_TDB jansson common_auth + ADOUBLE ''') bld.SAMBA3_BINARY('mvxattr',