summaryrefslogtreecommitdiff
path: root/source3/modules/vfs_snapper.c
diff options
context:
space:
mode:
authorDavid Disseldorp <ddiss@samba.org>2012-10-14 19:54:24 +0200
committerJeremy Allison <jra@samba.org>2015-03-31 18:40:25 +0200
commit9f7dd07d2658ac50ea3a1db8c4541ec3f87b8d6b (patch)
tree1e76d17d70b7ca9e04b38fdb5555b98f75f638fe /source3/modules/vfs_snapper.c
parent9b5b46a0921aa86f6b7da77e5735b7386e67af05 (diff)
downloadsamba-9f7dd07d2658ac50ea3a1db8c4541ec3f87b8d6b.tar.gz
vfs_snapper: create/delete snapshot support
Extend vfs_snapper to support the new remote snapshot creation and deletion hooks added for FSRVP. Signed-off-by: David Disseldorp <ddiss@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org>
Diffstat (limited to 'source3/modules/vfs_snapper.c')
-rw-r--r--source3/modules/vfs_snapper.c586
1 files changed, 586 insertions, 0 deletions
diff --git a/source3/modules/vfs_snapper.c b/source3/modules/vfs_snapper.c
index 415514aa1fd..e2e2587fa8c 100644
--- a/source3/modules/vfs_snapper.c
+++ b/source3/modules/vfs_snapper.c
@@ -40,6 +40,8 @@
#define SNAPPER_SIG_LIST_SNAPS_RSP "a(uquxussa{ss})"
#define SNAPPER_SIG_LIST_CONFS_RSP "a(ssa{ss})"
+#define SNAPPER_SIG_CREATE_SNAP_RSP "u"
+#define SNAPPER_SIG_DEL_SNAPS_RSP ""
#define SNAPPER_SIG_STRING_DICT "{ss}"
struct snapper_dict {
@@ -860,6 +862,296 @@ static NTSTATUS snapper_list_snaps_unpack(TALLOC_CTX *mem_ctx,
return NT_STATUS_OK;
}
+static NTSTATUS snapper_create_snap_pack(TALLOC_CTX *mem_ctx,
+ const char *snapper_conf,
+ const char *desc,
+ uint32_t num_user_data,
+ struct snapper_dict *user_data,
+ DBusMessage **req_msg_out)
+{
+ DBusMessage *msg;
+ DBusMessageIter args;
+ DBusMessageIter array_iter;
+ DBusMessageIter struct_iter;
+ const char *empty = "";
+ char *str_encoded;
+ uint32_t i;
+ bool ok;
+ TALLOC_CTX *enc_ctx;
+ NTSTATUS status;
+
+ DEBUG(10, ("CreateSingleSnapshot: %s, %s, %s, num user %u\n",
+ snapper_conf, desc, empty, num_user_data));
+
+ enc_ctx = talloc_new(mem_ctx);
+ if (enc_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg = dbus_message_new_method_call("org.opensuse.Snapper",
+ "/org/opensuse/Snapper",
+ "org.opensuse.Snapper",
+ "CreateSingleSnapshot");
+ if (msg == NULL) {
+ DEBUG(0, ("failed to create req msg\n"));
+ talloc_free(enc_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = snapper_dbus_str_encode(enc_ctx, snapper_conf, &str_encoded);
+ if (!NT_STATUS_IS_OK(status)) {
+ dbus_message_unref(msg);
+ talloc_free(enc_ctx);
+ return status;
+ }
+
+ /* append arguments */
+ dbus_message_iter_init_append(msg, &args);
+ ok = dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
+ &str_encoded);
+ if (!ok) {
+ dbus_message_unref(msg);
+ talloc_free(enc_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = snapper_dbus_str_encode(enc_ctx, desc, &str_encoded);
+ if (!NT_STATUS_IS_OK(status)) {
+ dbus_message_unref(msg);
+ talloc_free(enc_ctx);
+ return status;
+ }
+
+ ok = dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
+ &str_encoded);
+ if (!ok) {
+ dbus_message_unref(msg);
+ talloc_free(enc_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* cleanup - no need to encode empty string */
+ ok = dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
+ &empty);
+ if (!ok) {
+ dbus_message_unref(msg);
+ talloc_free(enc_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ok = dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY,
+ SNAPPER_SIG_STRING_DICT,
+ &array_iter);
+ if (!ok) {
+ dbus_message_unref(msg);
+ talloc_free(enc_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < num_user_data; i++) {
+ ok = dbus_message_iter_open_container(&array_iter,
+ DBUS_TYPE_DICT_ENTRY,
+ NULL, &struct_iter);
+ if (!ok) {
+ dbus_message_unref(msg);
+ talloc_free(enc_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = snapper_dbus_str_encode(enc_ctx, user_data[i].key,
+ &str_encoded);
+ if (!NT_STATUS_IS_OK(status)) {
+ dbus_message_unref(msg);
+ talloc_free(enc_ctx);
+ return status;
+ }
+
+ ok = dbus_message_iter_append_basic(&struct_iter,
+ DBUS_TYPE_STRING,
+ &str_encoded);
+ if (!ok) {
+ dbus_message_unref(msg);
+ talloc_free(enc_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = snapper_dbus_str_encode(enc_ctx, user_data[i].val,
+ &str_encoded);
+ if (!NT_STATUS_IS_OK(status)) {
+ dbus_message_unref(msg);
+ talloc_free(enc_ctx);
+ return status;
+ }
+
+ ok = dbus_message_iter_append_basic(&struct_iter,
+ DBUS_TYPE_STRING,
+ &str_encoded);
+ if (!ok) {
+ dbus_message_unref(msg);
+ talloc_free(enc_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ok = dbus_message_iter_close_container(&array_iter, &struct_iter);
+ if (!ok) {
+ dbus_message_unref(msg);
+ talloc_free(enc_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ ok = dbus_message_iter_close_container(&args, &array_iter);
+ if (!ok) {
+ dbus_message_unref(msg);
+ talloc_free(enc_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *req_msg_out = msg;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS snapper_create_snap_unpack(DBusConnection *conn,
+ DBusMessage *rsp_msg,
+ uint32_t *snap_id_out)
+{
+ NTSTATUS status;
+ DBusMessageIter iter;
+ int msg_type;
+ const char *sig;
+ uint32_t snap_id;
+
+ msg_type = dbus_message_get_type(rsp_msg);
+ if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
+ const char *err_str = dbus_message_get_error_name(rsp_msg);
+ DEBUG(0, ("create snap error response: %s, euid %d egid %d\n",
+ err_str, geteuid(), getegid()));
+ return snapper_err_ntstatus_map(err_str);
+ }
+
+ if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
+ DEBUG(0, ("unexpected create snap ret type: %d\n",
+ msg_type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sig = dbus_message_get_signature(rsp_msg);
+ if ((sig == NULL)
+ || (strcmp(sig, SNAPPER_SIG_CREATE_SNAP_RSP) != 0)) {
+ DEBUG(0, ("bad create snap response sig: %s, expected: %s\n",
+ (sig ? sig : "NULL"), SNAPPER_SIG_CREATE_SNAP_RSP));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* read the parameters */
+ if (!dbus_message_iter_init(rsp_msg, &iter)) {
+ DEBUG(0, ("response has no arguments!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = snapper_type_check_get(&iter, DBUS_TYPE_UINT32, &snap_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ *snap_id_out = snap_id;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS snapper_del_snap_pack(TALLOC_CTX *mem_ctx,
+ const char *snapper_conf,
+ uint32_t snap_id,
+ DBusMessage **req_msg_out)
+{
+ DBusMessage *msg;
+ DBusMessageIter args;
+ DBusMessageIter array_iter;
+ char *conf_encoded;
+ bool ok;
+ NTSTATUS status;
+
+ msg = dbus_message_new_method_call("org.opensuse.Snapper",
+ "/org/opensuse/Snapper",
+ "org.opensuse.Snapper",
+ "DeleteSnapshots");
+ if (msg == NULL) {
+ DEBUG(0, ("failed to create req msg\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = snapper_dbus_str_encode(mem_ctx, snapper_conf, &conf_encoded);
+ if (!NT_STATUS_IS_OK(status)) {
+ dbus_message_unref(msg);
+ return status;
+ }
+
+ /* append arguments */
+ dbus_message_iter_init_append(msg, &args);
+ ok = dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
+ &conf_encoded);
+ if (!ok) {
+ talloc_free(conf_encoded);
+ dbus_message_unref(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ok = dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_UINT32_AS_STRING,
+ &array_iter);
+ if (!ok) {
+ talloc_free(conf_encoded);
+ dbus_message_unref(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ok = dbus_message_iter_append_basic(&array_iter,
+ DBUS_TYPE_UINT32,
+ &snap_id);
+ if (!ok) {
+ talloc_free(conf_encoded);
+ dbus_message_unref(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dbus_message_iter_close_container(&args, &array_iter);
+ *req_msg_out = msg;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS snapper_del_snap_unpack(DBusConnection *conn,
+ DBusMessage *rsp_msg)
+{
+ int msg_type;
+ const char *sig;
+
+ msg_type = dbus_message_get_type(rsp_msg);
+ if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
+ const char *err_str = dbus_message_get_error_name(rsp_msg);
+ DEBUG(0, ("del snap error response: %s\n", err_str));
+ return snapper_err_ntstatus_map(err_str);
+ }
+
+ if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
+ DEBUG(0, ("unexpected del snap ret type: %d\n",
+ msg_type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sig = dbus_message_get_signature(rsp_msg);
+ if ((sig == NULL)
+ || (strcmp(sig, SNAPPER_SIG_DEL_SNAPS_RSP) != 0)) {
+ DEBUG(0, ("bad create snap response sig: %s, expected: %s\n",
+ (sig ? sig : "NULL"), SNAPPER_SIG_DEL_SNAPS_RSP));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* no parameters in response */
+
+ return NT_STATUS_OK;
+}
+
static NTSTATUS snapper_list_snaps_at_time_pack(TALLOC_CTX *mem_ctx,
const char *snapper_conf,
time_t time_lower,
@@ -915,6 +1207,61 @@ static NTSTATUS snapper_list_snaps_at_time_pack(TALLOC_CTX *mem_ctx,
/* no snapper_list_snaps_at_time_unpack, use snapper_list_snaps_unpack */
/*
+ * Determine the snapper snapshot id given a path.
+ * Ideally this should be determined via a lookup.
+ */
+static NTSTATUS snapper_snap_path_to_id(TALLOC_CTX *mem_ctx,
+ const char *snap_path,
+ uint32_t *snap_id_out)
+{
+ char *path_dup;
+ char *str_idx;
+ char *str_end;
+ uint32_t snap_id;
+
+ path_dup = talloc_strdup(mem_ctx, snap_path);
+ if (path_dup == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* trim trailing '/' */
+ str_idx = path_dup + strlen(path_dup) - 1;
+ while (*str_idx == '/') {
+ *str_idx = '\0';
+ str_idx--;
+ }
+
+ str_idx = strrchr(path_dup, '/');
+ if ((str_idx == NULL)
+ || (strcmp(str_idx + 1, "snapshot") != 0)) {
+ talloc_free(path_dup);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ while (*str_idx == '/') {
+ *str_idx = '\0';
+ str_idx--;
+ }
+
+ str_idx = strrchr(path_dup, '/');
+ if (str_idx == NULL) {
+ talloc_free(path_dup);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ str_idx++;
+ snap_id = strtoul(str_idx, &str_end, 10);
+ if (str_idx == str_end) {
+ talloc_free(path_dup);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ talloc_free(path_dup);
+ *snap_id_out = snap_id;
+ return NT_STATUS_OK;
+}
+
+/*
* Determine the snapper snapshot path given an id and base.
* Ideally this should be determined via a lookup.
*/
@@ -1009,6 +1356,242 @@ err_out:
return status;
}
+/*
+ * Check whether a path can be shadow copied. Return the base volume, allowing
+ * the caller to determine if multiple paths lie on the same base volume.
+ */
+static NTSTATUS snapper_snap_check_path(struct vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ const char *service_path,
+ char **base_volume)
+{
+ NTSTATUS status;
+ DBusConnection *dconn;
+ char *conf_name;
+ char *base_path;
+
+ dconn = snapper_dbus_conn_create();
+ if (dconn == NULL) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = snapper_get_conf_call(mem_ctx, dconn, service_path,
+ &conf_name, &base_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_conn_close;
+ }
+
+ talloc_free(conf_name);
+ *base_volume = base_path;
+ snapper_dbus_conn_destroy(dconn);
+
+ return NT_STATUS_OK;
+
+err_conn_close:
+ snapper_dbus_conn_destroy(dconn);
+ return status;
+}
+
+static NTSTATUS snapper_create_snap_call(TALLOC_CTX *mem_ctx,
+ DBusConnection *dconn,
+ const char *conf_name,
+ const char *base_path,
+ const char *snap_desc,
+ uint32_t num_user_data,
+ struct snapper_dict *user_data,
+ char **snap_path_out)
+{
+ NTSTATUS status;
+ DBusMessage *req_msg;
+ DBusMessage *rsp_msg;
+ uint32_t snap_id;
+ char *snap_path;
+
+ status = snapper_create_snap_pack(mem_ctx,
+ conf_name,
+ snap_desc,
+ num_user_data,
+ user_data,
+ &req_msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_out;
+ }
+
+ status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_req_free;
+ }
+
+ status = snapper_create_snap_unpack(dconn, rsp_msg, &snap_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_rsp_free;
+ }
+
+ status = snapper_snap_id_to_path(mem_ctx, base_path, snap_id,
+ &snap_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_rsp_free;
+ }
+
+ dbus_message_unref(rsp_msg);
+ dbus_message_unref(req_msg);
+
+ DEBUG(6, ("created new snapshot %u at %s\n", snap_id, snap_path));
+ *snap_path_out = snap_path;
+
+ return NT_STATUS_OK;
+
+err_rsp_free:
+ dbus_message_unref(rsp_msg);
+err_req_free:
+ dbus_message_unref(req_msg);
+err_out:
+ return status;
+}
+
+static NTSTATUS snapper_snap_create(struct vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ const char *base_volume,
+ time_t *tstamp,
+ bool rw,
+ char **_base_path,
+ char **_snap_path)
+{
+ DBusConnection *dconn;
+ NTSTATUS status;
+ char *conf_name;
+ char *base_path;
+ char *snap_path;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dconn = snapper_dbus_conn_create();
+ if (dconn == NULL) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = snapper_get_conf_call(tmp_ctx, dconn, base_volume,
+ &conf_name, &base_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ snapper_dbus_conn_destroy(dconn);
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ status = snapper_create_snap_call(tmp_ctx, dconn,
+ conf_name, base_path,
+ "Snapshot created by Samba",
+ 0, NULL,
+ &snap_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ snapper_dbus_conn_destroy(dconn);
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ snapper_dbus_conn_destroy(dconn);
+ *_base_path = talloc_steal(mem_ctx, base_path);
+ *_snap_path = talloc_steal(mem_ctx, snap_path);
+ talloc_free(tmp_ctx);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS snapper_delete_snap_call(TALLOC_CTX *mem_ctx,
+ DBusConnection *dconn,
+ const char *conf_name,
+ uint32_t snap_id)
+{
+ NTSTATUS status;
+ DBusMessage *req_msg;
+ DBusMessage *rsp_msg;
+
+ status = snapper_del_snap_pack(mem_ctx, conf_name, snap_id, &req_msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_out;
+ }
+
+ status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_req_free;
+ }
+
+ status = snapper_del_snap_unpack(dconn, rsp_msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_rsp_free;
+ }
+
+ dbus_message_unref(rsp_msg);
+ dbus_message_unref(req_msg);
+
+ DEBUG(6, ("deleted snapshot %u\n", snap_id));
+
+ return NT_STATUS_OK;
+
+err_rsp_free:
+ dbus_message_unref(rsp_msg);
+err_req_free:
+ dbus_message_unref(req_msg);
+err_out:
+ return status;
+}
+
+static NTSTATUS snapper_snap_delete(struct vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ char *base_path,
+ char *snap_path)
+{
+ DBusConnection *dconn;
+ NTSTATUS status;
+ char *conf_name;
+ char *snap_base_path;
+ uint32_t snap_id;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dconn = snapper_dbus_conn_create();
+ if (dconn == NULL) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = snapper_get_conf_call(tmp_ctx, dconn, base_path,
+ &conf_name, &snap_base_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ snapper_dbus_conn_destroy(dconn);
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ status = snapper_snap_path_to_id(tmp_ctx, snap_path, &snap_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ snapper_dbus_conn_destroy(dconn);
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ status = snapper_delete_snap_call(tmp_ctx, dconn, conf_name, snap_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ snapper_dbus_conn_destroy(dconn);
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ snapper_dbus_conn_destroy(dconn);
+ talloc_free(tmp_ctx);
+
+ return NT_STATUS_OK;
+}
+
/* sc_data used as parent talloc context for all labels */
static int snapper_get_shadow_copy_data(struct vfs_handle_struct *handle,
struct files_struct *fsp,
@@ -2186,6 +2769,9 @@ static uint64_t snapper_gmt_disk_free(vfs_handle_struct *handle,
static struct vfs_fn_pointers snapper_fns = {
+ .snap_check_path_fn = snapper_snap_check_path,
+ .snap_create_fn = snapper_snap_create,
+ .snap_delete_fn = snapper_snap_delete,
.get_shadow_copy_data_fn = snapper_get_shadow_copy_data,
.opendir_fn = snapper_gmt_opendir,
.disk_free_fn = snapper_gmt_disk_free,