be compatible with the conversion specifications recognized
by str[fp]time. The default value is "@GMT-%Y.%m.%d-%H.%M.%S".
+ shadow:sscanf = yes/no (default is no)
+
+ The time is the unsigned long integer (%lu) in the format string
+ rather than a time strptime() can parse. The result must be a unix time_t
+ time.
+
shadow:localtime = yes/no (default is no)
This is an optional parameter that indicates whether the
#include "includes.h"
#include "system/filesys.h"
#include "include/ntioctl.h"
-#include "smbd/proto.h"
#include <ccan/hash/hash.h>
#include "util_tdb.h"
-#define GMT_NAME_LEN 24 /* length of a @GMT- name */
-#define GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
+struct shadow_copy2_config {
+ char *gmt_format;
+ bool use_sscanf;
+ bool use_localtime;
+ char *snapdir;
+ bool snapdirseverywhere;
+ bool crossmountpoints;
+ bool fixinodes;
+ char *sort_order;
+ bool snapdir_absolute;
+};
static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
size_t **poffsets,
return true;
}
+/**
+ * Given a timstamp, build the string to insert into a path
+ * as a path component for creating the local path to the
+ * snapshot at the given timestamp of the input path.
+ */
static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
struct vfs_handle_struct *handle,
time_t snapshot)
{
struct tm snap_tm;
- fstring gmt;
- size_t gmt_len;
-
- if (localtime_r(&snapshot, &snap_tm) == 0) {
- DEBUG(10, ("gmtime_r failed\n"));
- return NULL;
- }
- gmt_len = strftime(gmt, sizeof(gmt),
- lp_parm_const_string(SNUM(handle->conn), "shadow",
- "format", GMT_FORMAT),
- &snap_tm);
- if (gmt_len == 0) {
- DEBUG(10, ("strftime failed\n"));
- return NULL;
+ fstring snaptime_string;
+ size_t snaptime_len;
+ struct shadow_copy2_config *config;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
+ return NULL);
+
+ if (config->use_sscanf) {
+ snaptime_len = snprintf(snaptime_string,
+ sizeof(snaptime_string),
+ config->gmt_format,
+ (unsigned long)snapshot);
+ if (snaptime_len <= 0) {
+ DEBUG(10, ("snprintf failed\n"));
+ return NULL;
+ }
+ } else {
+ if (config->use_localtime) {
+ if (localtime_r(&snapshot, &snap_tm) == 0) {
+ DEBUG(10, ("gmtime_r failed\n"));
+ return NULL;
+ }
+ } else {
+ if (gmtime_r(&snapshot, &snap_tm) == 0) {
+ DEBUG(10, ("gmtime_r failed\n"));
+ return NULL;
+ }
+ }
+ snaptime_len = strftime(snaptime_string,
+ sizeof(snaptime_string),
+ config->gmt_format,
+ &snap_tm);
+ if (snaptime_len == 0) {
+ DEBUG(10, ("strftime failed\n"));
+ return NULL;
+ }
}
- return talloc_asprintf(talloc_tos(), "/%s/%s",
- lp_parm_const_string(
- SNUM(handle->conn), "shadow", "snapdir",
- ".snapshots"),
- gmt);
+ return talloc_asprintf(mem_ctx, "/%s/%s",
+ config->snapdir, snaptime_string);
}
+/**
+ * Strip a snapshot component from an filename as
+ * handed in via the smb layer.
+ * Returns the parsed timestamp and the stripped filename.
+ */
static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
struct vfs_handle_struct *handle,
const char *name,
char *q;
char *stripped;
size_t rest_len, dst_len;
+ struct shadow_copy2_config *config;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
+ return false);
p = strstr_m(name, "@GMT-");
if (p == NULL) {
goto no_snapshot;
}
tm.tm_isdst = -1;
- timestamp = mktime(&tm);
+ timestamp = timegm(&tm);
if (timestamp == (time_t)-1) {
goto no_snapshot;
}
rest_len = strlen(q);
dst_len = (p-name) + rest_len;
- if (lp_parm_bool(SNUM(handle->conn), "shadow", "snapdirseverywhere",
- false)) {
+ if (config->snapdirseverywhere) {
char *insert;
bool have_insert;
insert = shadow_copy2_insert_string(talloc_tos(), handle,
return path;
}
+/**
+ * Convert from a name as handed in via the SMB layer
+ * and a timestamp into the local path of the snapshot
+ * of the provided file at the provided time.
+ */
static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
struct vfs_handle_struct *handle,
const char *name, time_t timestamp)
size_t insertlen;
int i, saved_errno;
size_t min_offset;
+ struct shadow_copy2_config *config;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
+ return NULL);
path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
name);
min_offset = 0;
- if (!lp_parm_bool(SNUM(handle->conn), "shadow", "crossmountpoints",
- false)) {
+ if (!config->crossmountpoints) {
char *mount_point;
mount_point = shadow_copy2_find_mount_point(talloc_tos(),
static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
SMB_STRUCT_STAT *sbuf)
{
- if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
+ struct shadow_copy2_config *config;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
+ return);
+
+ if (config->fixinodes) {
/* some snapshot systems, like GPFS, return the name
device:inode for the snapshot files as the current
files. That breaks the 'restore' button in the shadow copy
char *stripped;
int ret, saved_errno;
struct smb_filename *conv;
- NTSTATUS status;
if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
smb_fname->base_name,
if (timestamp == 0) {
return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
}
- status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
- if (!NT_STATUS_IS_OK(status)) {
+ conv = cp_smb_filename(talloc_tos(), smb_fname);
+ if (conv == NULL) {
errno = ENOMEM;
return -1;
}
char *stripped;
int ret, saved_errno;
struct smb_filename *conv;
- NTSTATUS status;
if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
smb_fname->base_name,
if (timestamp == 0) {
return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
}
- status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
- if (!NT_STATUS_IS_OK(status)) {
+ conv = cp_smb_filename(talloc_tos(), smb_fname);
+ if (conv == NULL) {
errno = ENOMEM;
return -1;
}
return result;
}
+/**
+ * Check whether a given directory contains a
+ * snapshot directory as direct subdirectory.
+ * If yes, return the path of the snapshot-subdir,
+ * otherwise return NULL.
+ */
static char *have_snapdir(struct vfs_handle_struct *handle,
const char *path)
{
struct smb_filename smb_fname;
int ret;
+ struct shadow_copy2_config *config;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
+ return NULL);
ZERO_STRUCT(smb_fname);
- smb_fname.base_name = talloc_asprintf(
- talloc_tos(), "%s/%s", path,
- lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir",
- ".snapshots"));
+ smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
+ path, config->snapdir);
if (smb_fname.base_name == NULL) {
return NULL;
}
return NULL;
}
+/**
+ * Find the snapshot directory (if any) for the given
+ * filename (which is relative to the share).
+ */
static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
struct vfs_handle_struct *handle,
struct smb_filename *smb_fname)
{
char *path, *p;
char *snapdir;
+ struct shadow_copy2_config *config;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
+ return NULL);
path = talloc_asprintf(mem_ctx, "%s/%s",
handle->conn->connectpath,
return NULL;
}
-static bool shadow_copy2_snapshot_to_gmt(TALLOC_CTX *mem_ctx,
- vfs_handle_struct *handle,
+static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
const char *name,
char *gmt, size_t gmt_len)
{
struct tm timestamp;
time_t timestamp_t;
+ unsigned long int timestamp_long;
const char *fmt;
+ struct shadow_copy2_config *config;
- fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
- "format", GMT_FORMAT);
+ SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
+ return NULL);
+
+ fmt = config->gmt_format;
ZERO_STRUCT(timestamp);
- if (strptime(name, fmt, ×tamp) == NULL) {
- DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
+ if (config->use_sscanf) {
+ if (sscanf(name, fmt, ×tamp_long) != 1) {
+ DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
+ "no sscanf match %s: %s\n",
+ fmt, name));
+ return false;
+ }
+ timestamp_t = timestamp_long;
+ gmtime_r(×tamp_t, ×tamp);
+ } else {
+ if (strptime(name, fmt, ×tamp) == NULL) {
+ DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
+ "no match %s: %s\n",
+ fmt, name));
+ return false;
+ }
+ DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
fmt, name));
- return false;
+
+ if (config->use_localtime) {
+ timestamp.tm_isdst = -1;
+ timestamp_t = mktime(×tamp);
+ gmtime_r(×tamp_t, ×tamp);
+ }
}
- DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name));
-
- if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
- timestamp.tm_isdst = -1;
- timestamp_t = mktime(×tamp);
- gmtime_r(×tamp_t, ×tamp);
- }
strftime(gmt, gmt_len, GMT_FORMAT, ×tamp);
return true;
}
{
int (*cmpfunc)(const void *, const void *);
const char *sort;
+ struct shadow_copy2_config *config;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
+ return);
- sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
- "sort", NULL);
+ sort = config->sort_order;
if (sort == NULL) {
return;
}
shadow_copy2_data->num_volumes,
cmpfunc);
}
-
- return;
}
static int shadow_copy2_get_shadow_copy_data(
* directory
*/
if (!shadow_copy2_snapshot_to_gmt(
- tmp_ctx, handle, d->d_name,
+ handle, d->d_name,
snapshot, sizeof(snapshot))) {
DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
struct files_struct *fsp,
uint32 security_info,
+ TALLOC_CTX *mem_ctx,
struct security_descriptor **ppdesc)
{
time_t timestamp;
}
if (timestamp == 0) {
return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
+ mem_ctx,
ppdesc);
}
conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
if (conv == NULL) {
return map_nt_error_from_unix(errno);
}
- status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info, ppdesc);
+ status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
+ mem_ctx, ppdesc);
TALLOC_FREE(conv);
return status;
}
static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
const char *fname,
uint32 security_info,
+ TALLOC_CTX *mem_ctx,
struct security_descriptor **ppdesc)
{
time_t timestamp;
}
if (timestamp == 0) {
return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
- ppdesc);
+ mem_ctx, ppdesc);
}
conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
TALLOC_FREE(stripped);
if (conv == NULL) {
return map_nt_error_from_unix(errno);
}
- status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info, ppdesc);
+ status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
+ mem_ctx, ppdesc);
TALLOC_FREE(conv);
return status;
}
return ret;
}
+static int shadow_copy2_connect(struct vfs_handle_struct *handle,
+ const char *service, const char *user)
+{
+ struct shadow_copy2_config *config;
+ int ret;
+ const char *snapdir;
+ const char *gmt_format;
+ const char *sort_order;
+
+ DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
+ (unsigned)handle->conn->cnum,
+ handle->conn->connectpath));
+
+ ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
+ if (ret < 0) {
+ return ret;
+ }
+
+ config = talloc_zero(handle->conn, struct shadow_copy2_config);
+ if (config == NULL) {
+ DEBUG(0, ("talloc_zero() failed\n"));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ gmt_format = lp_parm_const_string(SNUM(handle->conn),
+ "shadow", "format",
+ GMT_FORMAT);
+ config->gmt_format = talloc_strdup(config, gmt_format);
+ if (config->gmt_format == NULL) {
+ DEBUG(0, ("talloc_strdup() failed\n"));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
+ "shadow", "sscanf", false);
+
+ config->use_localtime = lp_parm_bool(SNUM(handle->conn),
+ "shadow", "localtime",
+ false);
+
+ snapdir = lp_parm_const_string(SNUM(handle->conn),
+ "shadow", "snapdir",
+ ".snapshots");
+ config->snapdir = talloc_strdup(config, snapdir);
+ if (config->snapdir == NULL) {
+ DEBUG(0, ("talloc_strdup() failed\n"));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
+ "shadow",
+ "snapdirseverywhere",
+ false);
+
+ config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
+ "shadow", "crossmountpoints",
+ false);
+
+ config->fixinodes = lp_parm_bool(SNUM(handle->conn),
+ "shadow", "fixinodes",
+ false);
+
+ sort_order = lp_parm_const_string(SNUM(handle->conn),
+ "shadow", "sort", "desc");
+ config->sort_order = talloc_strdup(config, sort_order);
+ if (config->sort_order == NULL) {
+ DEBUG(0, ("talloc_strdup() failed\n"));
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (config->snapdir[0] == '/') {
+ config->snapdir_absolute = true;
+ if (config->snapdirseverywhere == true) {
+ DEBUG(1, (__location__ " Warning: An absolute snapdir "
+ "is incompatible with 'snapdirseverywhere', "
+ "setting 'snapdirseverywhere' to false.\n"));
+ config->snapdirseverywhere = false;
+ }
+
+ if (config->crossmountpoints == true) {
+ DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
+ "is not supported with an absolute snapdir. "
+ "Disabling it.\n"));
+ config->crossmountpoints = false;
+ }
+ }
+
+ SMB_VFS_HANDLE_SET_DATA(handle, config,
+ NULL, struct shadow_copy2_config,
+ return -1);
+
+ return 0;
+}
static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
+ .connect_fn = shadow_copy2_connect,
.opendir_fn = shadow_copy2_opendir,
.rename_fn = shadow_copy2_rename,
.link_fn = shadow_copy2_link,