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 <tdb.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
without significant VFS changes
*/
uint32_t shash;
- TDB_DATA data = string_tdb_data(fname);
- shash = tdb_jenkins_hash(&data) & 0xFF000000;
+ shash = hash(fname, strlen(fname), 0) & 0xFF000000;
if (shash == 0) {
shash = 1;
}
}
}
-static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
+static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
const char *fname,
const char *mask,
uint32 attr)
{
time_t timestamp;
char *stripped;
- SMB_STRUCT_DIR *ret;
+ DIR *ret;
int saved_errno;
char *conv;
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;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
+ return NULL);
- fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
- "format", GMT_FORMAT);
+ 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(
struct shadow_copy_data *shadow_copy2_data,
bool labels)
{
- SMB_STRUCT_DIR *p;
+ DIR *p;
const char *snapdir;
- SMB_STRUCT_DIRENT *d;
+ struct dirent *d;
TALLOC_CTX *tmp_ctx = talloc_stackframe();
snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
* 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 ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
- const char *fname, const char *aname,
- void *value, size_t size)
-{
- time_t timestamp;
- char *stripped;
- ssize_t ret;
- int saved_errno;
- char *conv;
-
- if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
- ×tamp, &stripped)) {
- return -1;
- }
- if (timestamp == 0) {
- return SMB_VFS_NEXT_LGETXATTR(handle, fname, aname, value,
- size);
- }
- conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
- TALLOC_FREE(stripped);
- if (conv == NULL) {
- return -1;
- }
- ret = SMB_VFS_NEXT_LGETXATTR(handle, conv, aname, value, size);
- saved_errno = errno;
- TALLOC_FREE(conv);
- errno = saved_errno;
- return ret;
-}
-
static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
const char *fname,
char *list, size_t size)
return ret;
}
-static int shadow_copy2_lremovexattr(vfs_handle_struct *handle,
- const char *fname, const char *aname)
-{
- time_t timestamp;
- char *stripped;
- int ret, saved_errno;
- char *conv;
-
- if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
- ×tamp, &stripped)) {
- return -1;
- }
- if (timestamp == 0) {
- return SMB_VFS_NEXT_LREMOVEXATTR(handle, fname, aname);
- }
- conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
- TALLOC_FREE(stripped);
- if (conv == NULL) {
- return -1;
- }
- ret = SMB_VFS_NEXT_LREMOVEXATTR(handle, conv, aname);
- saved_errno = errno;
- TALLOC_FREE(conv);
- errno = saved_errno;
- return ret;
-}
-
static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
const char *fname,
const char *aname, const void *value,
return ret;
}
-static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle,
- const char *fname,
- const char *aname, const void *value,
- size_t size, int flags)
-{
- time_t timestamp;
- char *stripped;
- ssize_t ret;
- int saved_errno;
- char *conv;
-
- if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
- ×tamp, &stripped)) {
- return -1;
- }
- if (timestamp == 0) {
- return SMB_VFS_NEXT_LSETXATTR(handle, fname, aname, value,
- size, flags);
- }
- conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
- TALLOC_FREE(stripped);
- if (conv == NULL) {
- return -1;
- }
- ret = SMB_VFS_NEXT_LSETXATTR(handle, conv, aname, value, size, flags);
- saved_errno = errno;
- TALLOC_FREE(conv);
- errno = saved_errno;
- return ret;
-}
-
static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
const char *fname, mode_t mode)
{
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 = {
- .opendir = shadow_copy2_opendir,
- .rename = shadow_copy2_rename,
- .link = shadow_copy2_link,
- .symlink = shadow_copy2_symlink,
- .stat = shadow_copy2_stat,
- .lstat = shadow_copy2_lstat,
- .fstat = shadow_copy2_fstat,
+ .connect_fn = shadow_copy2_connect,
+ .opendir_fn = shadow_copy2_opendir,
+ .rename_fn = shadow_copy2_rename,
+ .link_fn = shadow_copy2_link,
+ .symlink_fn = shadow_copy2_symlink,
+ .stat_fn = shadow_copy2_stat,
+ .lstat_fn = shadow_copy2_lstat,
+ .fstat_fn = shadow_copy2_fstat,
.open_fn = shadow_copy2_open,
- .unlink = shadow_copy2_unlink,
- .chmod = shadow_copy2_chmod,
- .chown = shadow_copy2_chown,
- .chdir = shadow_copy2_chdir,
- .ntimes = shadow_copy2_ntimes,
- .vfs_readlink = shadow_copy2_readlink,
- .mknod = shadow_copy2_mknod,
- .realpath = shadow_copy2_realpath,
- .get_nt_acl = shadow_copy2_get_nt_acl,
- .fget_nt_acl = shadow_copy2_fget_nt_acl,
- .get_shadow_copy_data = shadow_copy2_get_shadow_copy_data,
- .mkdir = shadow_copy2_mkdir,
- .rmdir = shadow_copy2_rmdir,
- .getxattr = shadow_copy2_getxattr,
- .lgetxattr = shadow_copy2_lgetxattr,
- .listxattr = shadow_copy2_listxattr,
- .removexattr = shadow_copy2_removexattr,
- .lremovexattr = shadow_copy2_lremovexattr,
- .setxattr = shadow_copy2_setxattr,
- .lsetxattr = shadow_copy2_lsetxattr,
- .chmod_acl = shadow_copy2_chmod_acl,
- .chflags = shadow_copy2_chflags,
- .get_real_filename = shadow_copy2_get_real_filename,
+ .unlink_fn = shadow_copy2_unlink,
+ .chmod_fn = shadow_copy2_chmod,
+ .chown_fn = shadow_copy2_chown,
+ .chdir_fn = shadow_copy2_chdir,
+ .ntimes_fn = shadow_copy2_ntimes,
+ .readlink_fn = shadow_copy2_readlink,
+ .mknod_fn = shadow_copy2_mknod,
+ .realpath_fn = shadow_copy2_realpath,
+ .get_nt_acl_fn = shadow_copy2_get_nt_acl,
+ .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
+ .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
+ .mkdir_fn = shadow_copy2_mkdir,
+ .rmdir_fn = shadow_copy2_rmdir,
+ .getxattr_fn = shadow_copy2_getxattr,
+ .listxattr_fn = shadow_copy2_listxattr,
+ .removexattr_fn = shadow_copy2_removexattr,
+ .setxattr_fn = shadow_copy2_setxattr,
+ .chmod_acl_fn = shadow_copy2_chmod_acl,
+ .chflags_fn = shadow_copy2_chflags,
+ .get_real_filename_fn = shadow_copy2_get_real_filename,
};
NTSTATUS vfs_shadow_copy2_init(void);