diff options
author | David Disseldorp <ddiss@samba.org> | 2012-10-14 19:54:24 +0200 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2015-03-31 18:40:25 +0200 |
commit | 9f7dd07d2658ac50ea3a1db8c4541ec3f87b8d6b (patch) | |
tree | 1e76d17d70b7ca9e04b38fdb5555b98f75f638fe /source3/modules/vfs_snapper.c | |
parent | 9b5b46a0921aa86f6b7da77e5735b7386e67af05 (diff) | |
download | samba-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.c | 586 |
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, |