*/
/*
- * This is a second implemetation of a shadow copy module for exposing
+ * This is a second implementation of a shadow copy module for exposing
* file system snapshots to windows clients as shadow copies.
*
* See the manual page for documentation.
time_t fetch_time; /* snaplist update time */
};
-
/*
* shadow_copy2 private structure. This structure will be
* used to keep module specific information
bool labels);
/**
- *This function will create a new snapshot list entry and
+ * This function will create a new snapshot list entry and
* return to the caller. This entry will also be added to
* the global snapshot list.
*
}
/**
- *This function will delete the entire snaplist and reset
+ * This function will delete the entire snaplist and reset
* priv->snaps->snaplist to NULL.
*
* @param[in] priv shadow_copye specific data structure
config->snapdir, snaptime_string);
}
if (result == NULL) {
- DEBUG(1, (__location__ " talloc_asprintf failed\n"));
+ DBG_WARNING("talloc_asprintf failed\n");
}
return result;
result = talloc_asprintf(mem_ctx, "%s/%s",
priv->config->snapshot_basepath, snaptime_string);
if (result == NULL) {
- DEBUG(1, (__location__ " talloc_asprintf failed\n"));
+ DBG_WARNING("talloc_asprintf failed\n");
}
return result;
* This function does two things.
*
* 1). Checks if an incoming filename is already a
- * snapshot converted pathname.
+ * snapshot converted pathname.
* If so, it returns the pathname truncated
* at the snapshot point which will be used
* as the connectpath, and then does an early return.
*
* 2). Checks if an incoming filename contains an
- * SMB-layer @GMT- style timestamp.
+ * SMB-layer @GMT- style timestamp.
* If so, it strips the timestamp, and returns
* both the timestamp and the stripped path
* (making it cwd-relative).
goto fail;
}
- ZERO_STRUCT(converted_fname);
- converted_fname.base_name = converted;
+ converted_fname = (struct smb_filename) {
+ .base_name = converted,
+ };
ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
DEBUG(10, ("Trying[not snapdirseverywhere] %s: %d (%s)\n",
insertlen = talloc_get_size(insert)-1;
/*
- * Note: We deliberatly don't expensively initialize the
+ * Note: We deliberately don't expensively initialize the
* array with talloc_zero here: Putting zero into
* converted[pathlen+insertlen] below is sufficient, because
* in the following for loop, the insert string is inserted
memcpy(converted, path, pathlen+1);
converted[pathlen+insertlen] = '\0';
- ZERO_STRUCT(converted_fname);
- converted_fname.base_name = converted;
+ converted_fname = (struct smb_filename) {
+ .base_name = converted,
+ };
for (i = num_slashes-1; i>=0; i--) {
int ret;
return);
if (priv->config->fixinodes) {
- /* some snapshot systems, like GPFS, return the name
+ /* some snapshot systems, like GPFS, return the same
device:inode for the snapshot files as the current
files. That breaks the 'restore' button in the shadow copy
GUI, as the client gets a sharing violation.
static int shadow_copy2_stat(vfs_handle_struct *handle,
struct smb_filename *smb_fname)
{
+ struct shadow_copy2_private *priv = NULL;
time_t timestamp = 0;
char *stripped = NULL;
+ bool converted = false;
+ char *abspath = NULL;
char *tmp;
- int saved_errno = 0;
- int ret;
+ int ret = 0;
- if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
- smb_fname,
- ×tamp, &stripped)) {
+ SMB_VFS_HANDLE_GET_DATA(handle, priv, struct shadow_copy2_private,
+ return -1);
+
+ if (!shadow_copy2_strip_snapshot_converted(talloc_tos(),
+ handle,
+ smb_fname,
+ ×tamp,
+ &stripped,
+ &converted)) {
return -1;
}
if (timestamp == 0) {
- return SMB_VFS_NEXT_STAT(handle, smb_fname);
+ TALLOC_FREE(stripped);
+ ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
+ if (ret != 0) {
+ return ret;
+ }
+ if (!converted) {
+ return 0;
+ }
+
+ abspath = make_path_absolute(talloc_tos(),
+ priv,
+ smb_fname->base_name);
+ if (abspath == NULL) {
+ return -1;
+ }
+
+ convert_sbuf(handle, abspath, &smb_fname->st);
+ TALLOC_FREE(abspath);
+ return 0;
}
tmp = smb_fname->base_name;
}
ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
- if (ret == -1) {
- saved_errno = errno;
+ if (ret != 0) {
+ goto out;
+ }
+
+ abspath = make_path_absolute(talloc_tos(),
+ priv,
+ smb_fname->base_name);
+ if (abspath == NULL) {
+ ret = -1;
+ goto out;
}
+ convert_sbuf(handle, abspath, &smb_fname->st);
+ TALLOC_FREE(abspath);
+
+out:
TALLOC_FREE(smb_fname->base_name);
smb_fname->base_name = tmp;
- if (ret == 0) {
- convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
- }
- if (saved_errno != 0) {
- errno = saved_errno;
- }
return ret;
}
static int shadow_copy2_lstat(vfs_handle_struct *handle,
struct smb_filename *smb_fname)
{
+ struct shadow_copy2_private *priv = NULL;
time_t timestamp = 0;
char *stripped = NULL;
+ bool converted = false;
+ char *abspath = NULL;
char *tmp;
- int saved_errno = 0;
- int ret;
+ int ret = 0;
- if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
- smb_fname,
- ×tamp, &stripped)) {
+ SMB_VFS_HANDLE_GET_DATA(handle, priv, struct shadow_copy2_private,
+ return -1);
+
+ if (!shadow_copy2_strip_snapshot_converted(talloc_tos(),
+ handle,
+ smb_fname,
+ ×tamp,
+ &stripped,
+ &converted)) {
return -1;
}
if (timestamp == 0) {
- return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
+ TALLOC_FREE(stripped);
+ ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
+ if (ret != 0) {
+ return ret;
+ }
+ if (!converted) {
+ return 0;
+ }
+
+ abspath = make_path_absolute(talloc_tos(),
+ priv,
+ smb_fname->base_name);
+ if (abspath == NULL) {
+ return -1;
+ }
+
+ convert_sbuf(handle, abspath, &smb_fname->st);
+ TALLOC_FREE(abspath);
+ return 0;
}
tmp = smb_fname->base_name;
}
ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
- if (ret == -1) {
- saved_errno = errno;
+ if (ret != 0) {
+ goto out;
+ }
+
+ abspath = make_path_absolute(talloc_tos(),
+ priv,
+ smb_fname->base_name);
+ if (abspath == NULL) {
+ ret = -1;
+ goto out;
}
+ convert_sbuf(handle, abspath, &smb_fname->st);
+ TALLOC_FREE(abspath);
+
+out:
TALLOC_FREE(smb_fname->base_name);
smb_fname->base_name = tmp;
- if (ret == 0) {
- convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
- }
- if (saved_errno != 0) {
- errno = saved_errno;
- }
return ret;
}
static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
SMB_STRUCT_STAT *sbuf)
{
+ struct shadow_copy2_private *priv = NULL;
time_t timestamp = 0;
struct smb_filename *orig_smb_fname = NULL;
struct smb_filename vss_smb_fname;
struct smb_filename *orig_base_smb_fname = NULL;
struct smb_filename vss_base_smb_fname;
char *stripped = NULL;
- int saved_errno = 0;
+ char *abspath = NULL;
+ bool converted = false;
bool ok;
int ret;
- ok = shadow_copy2_strip_snapshot(talloc_tos(), handle,
- fsp->fsp_name,
- ×tamp, &stripped);
+ SMB_VFS_HANDLE_GET_DATA(handle, priv, struct shadow_copy2_private,
+ return -1);
+
+ ok = shadow_copy2_strip_snapshot_converted(talloc_tos(),
+ handle,
+ fsp->fsp_name,
+ ×tamp,
+ &stripped,
+ &converted);
if (!ok) {
return -1;
}
if (timestamp == 0) {
TALLOC_FREE(stripped);
- return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
+ ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
+ if (ret != 0) {
+ return ret;
+ }
+ if (!converted) {
+ return 0;
+ }
+
+ abspath = make_path_absolute(talloc_tos(),
+ priv,
+ fsp->fsp_name->base_name);
+ if (abspath == NULL) {
+ return -1;
+ }
+
+ convert_sbuf(handle, abspath, sbuf);
+ TALLOC_FREE(abspath);
+ return 0;
}
vss_smb_fname = *fsp->fsp_name;
orig_smb_fname = fsp->fsp_name;
fsp->fsp_name = &vss_smb_fname;
- if (fsp->base_fsp != NULL) {
+ if (fsp_is_alternate_stream(fsp)) {
vss_base_smb_fname = *fsp->base_fsp->fsp_name;
vss_base_smb_fname.base_name = vss_smb_fname.base_name;
orig_base_smb_fname = fsp->base_fsp->fsp_name;
}
ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
- fsp->fsp_name = orig_smb_fname;
- if (fsp->base_fsp != NULL) {
- fsp->base_fsp->fsp_name = orig_base_smb_fname;
- }
- if (ret == -1) {
- saved_errno = errno;
+ if (ret != 0) {
+ goto out;
}
- if (ret == 0) {
- convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
+ abspath = make_path_absolute(talloc_tos(),
+ priv,
+ fsp->fsp_name->base_name);
+ if (abspath == NULL) {
+ ret = -1;
+ goto out;
}
- if (saved_errno != 0) {
- errno = saved_errno;
+
+ convert_sbuf(handle, abspath, sbuf);
+ TALLOC_FREE(abspath);
+
+out:
+ fsp->fsp_name = orig_smb_fname;
+ if (fsp_is_alternate_stream(fsp)) {
+ fsp->base_fsp->fsp_name = orig_base_smb_fname;
}
+
return ret;
}
-static int shadow_copy2_openat(vfs_handle_struct *handle,
- const struct files_struct *dirfsp,
- const struct smb_filename *smb_fname_in,
- struct files_struct *fsp,
- int flags,
- mode_t mode)
+static int shadow_copy2_fstatat(
+ struct vfs_handle_struct *handle,
+ const struct files_struct *dirfsp,
+ const struct smb_filename *smb_fname_in,
+ SMB_STRUCT_STAT *sbuf,
+ int flags)
{
+ struct shadow_copy2_private *priv = NULL;
struct smb_filename *smb_fname = NULL;
time_t timestamp = 0;
char *stripped = NULL;
- bool is_converted = false;
- int saved_errno = 0;
+ char *abspath = NULL;
+ bool converted = false;
int ret;
bool ok;
+ SMB_VFS_HANDLE_GET_DATA(handle, priv, struct shadow_copy2_private,
+ return -1);
+
+ smb_fname = full_path_from_dirfsp_atname(talloc_tos(),
+ dirfsp,
+ smb_fname_in);
+ if (smb_fname == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
ok = shadow_copy2_strip_snapshot_converted(talloc_tos(),
handle,
- smb_fname_in,
+ smb_fname,
×tamp,
&stripped,
- &is_converted);
+ &converted);
if (!ok) {
return -1;
}
if (timestamp == 0) {
- if (is_converted) {
+ TALLOC_FREE(stripped);
+ ret = SMB_VFS_NEXT_FSTATAT(
+ handle, dirfsp, smb_fname_in, sbuf, flags);
+ if (ret != 0) {
+ return ret;
+ }
+ if (!converted) {
+ return 0;
+ }
+
+ abspath = make_path_absolute(
+ talloc_tos(), priv, smb_fname->base_name);
+ if (abspath == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ convert_sbuf(handle, abspath, sbuf);
+ TALLOC_FREE(abspath);
+ return 0;
+ }
+
+ smb_fname->base_name = shadow_copy2_convert(
+ smb_fname, handle, stripped, timestamp);
+ TALLOC_FREE(stripped);
+ if (smb_fname->base_name == NULL) {
+ TALLOC_FREE(smb_fname);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ ret = SMB_VFS_NEXT_FSTATAT(handle,
+ dirfsp,
+ smb_fname,
+ sbuf,
+ flags);
+ if (ret != 0) {
+ int saved_errno = errno;
+ TALLOC_FREE(smb_fname);
+ errno = saved_errno;
+ return -1;
+ }
+
+ abspath = make_path_absolute(
+ talloc_tos(), priv, smb_fname->base_name);
+ if (abspath == NULL) {
+ TALLOC_FREE(smb_fname);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ convert_sbuf(handle, abspath, sbuf);
+ TALLOC_FREE(abspath);
+
+ TALLOC_FREE(smb_fname);
+
+ return 0;
+}
+
+static struct smb_filename *shadow_copy2_openat_name(
+ TALLOC_CTX *mem_ctx,
+ const struct files_struct *dirfsp,
+ const struct files_struct *fsp,
+ const struct smb_filename *smb_fname_in)
+{
+ struct smb_filename *result = NULL;
+
+ if (fsp->base_fsp != NULL) {
+ struct smb_filename *base_fname = fsp->base_fsp->fsp_name;
+
+ if (smb_fname_in->base_name[0] == '/') {
/*
- * Just pave over the user requested mode and use
- * O_RDONLY. Later attempts by the client to write on
- * the handle will fail in the pwrite() syscall with
- * EINVAL which we carefully map to EROFS. In sum, this
- * matches Windows behaviour.
+ * Special-case stream names from streams_depot
*/
- flags = O_RDONLY;
+ result = cp_smb_filename(mem_ctx, smb_fname_in);
+ } else {
+
+ SMB_ASSERT(is_named_stream(smb_fname_in));
+
+ result = synthetic_smb_fname(mem_ctx,
+ base_fname->base_name,
+ smb_fname_in->stream_name,
+ &smb_fname_in->st,
+ smb_fname_in->twrp,
+ smb_fname_in->flags);
}
- return SMB_VFS_NEXT_OPENAT(handle,
- dirfsp,
- smb_fname_in,
- fsp,
- flags,
- mode);
+ } else {
+ result = full_path_from_dirfsp_atname(
+ mem_ctx, dirfsp, smb_fname_in);
+ }
+
+ return result;
+}
+
+static int shadow_copy2_openat(vfs_handle_struct *handle,
+ const struct files_struct *dirfsp,
+ const struct smb_filename *smb_fname_in,
+ struct files_struct *fsp,
+ const struct vfs_open_how *_how)
+{
+ struct vfs_open_how how = *_how;
+ struct smb_filename *smb_fname = NULL;
+ time_t timestamp = 0;
+ char *stripped = NULL;
+ int saved_errno = 0;
+ int ret;
+ bool ok;
+
+ if (how.resolve != 0) {
+ errno = ENOSYS;
+ return -1;
}
- smb_fname = cp_smb_filename(talloc_tos(), smb_fname_in);
+ smb_fname = shadow_copy2_openat_name(
+ talloc_tos(), dirfsp, fsp, smb_fname_in);
if (smb_fname == NULL) {
- TALLOC_FREE(stripped);
errno = ENOMEM;
return -1;
}
+ ok = shadow_copy2_strip_snapshot(talloc_tos(),
+ handle,
+ smb_fname,
+ ×tamp,
+ &stripped);
+ if (!ok) {
+ TALLOC_FREE(smb_fname);
+ return -1;
+ }
+ if (timestamp == 0) {
+ TALLOC_FREE(stripped);
+ TALLOC_FREE(smb_fname);
+ return SMB_VFS_NEXT_OPENAT(handle,
+ dirfsp,
+ smb_fname_in,
+ fsp,
+ &how);
+ }
+
smb_fname->base_name = shadow_copy2_convert(smb_fname,
handle,
stripped,
timestamp);
- TALLOC_FREE(stripped);
if (smb_fname->base_name == NULL) {
+ int err = errno;
+ TALLOC_FREE(stripped);
TALLOC_FREE(smb_fname);
- errno = ENOMEM;
+ errno = err;
return -1;
}
-
- /*
- * Just pave over the user requested mode and use O_RDONLY. Later
- * attempts by the client to write on the handle will fail in the
- * pwrite() syscall with EINVAL which we carefully map to EROFS. In sum,
- * this matches Windows behaviour.
- */
- flags = O_RDONLY;
+ TALLOC_FREE(stripped);
ret = SMB_VFS_NEXT_OPENAT(handle,
dirfsp,
smb_fname,
fsp,
- flags,
- mode);
+ &how);
if (ret == -1) {
saved_errno = errno;
}
flags);
}
-static int shadow_copy2_chmod(vfs_handle_struct *handle,
- const struct smb_filename *smb_fname,
- mode_t mode)
+static int shadow_copy2_fchmod(vfs_handle_struct *handle,
+ struct files_struct *fsp,
+ mode_t mode)
{
time_t timestamp = 0;
+ const struct smb_filename *smb_fname = NULL;
+ smb_fname = fsp->fsp_name;
if (!shadow_copy2_strip_snapshot(talloc_tos(),
- handle,
- smb_fname,
- ×tamp,
- NULL)) {
+ handle,
+ smb_fname,
+ ×tamp,
+ NULL)) {
return -1;
}
if (timestamp != 0) {
errno = EROFS;
return -1;
}
- return SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
+ return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
}
static void store_cwd_data(vfs_handle_struct *handle,
return ret;
}
-static int shadow_copy2_ntimes(vfs_handle_struct *handle,
- const struct smb_filename *smb_fname,
- struct smb_file_time *ft)
+static int shadow_copy2_fntimes(vfs_handle_struct *handle,
+ files_struct *fsp,
+ struct smb_file_time *ft)
{
time_t timestamp = 0;
- if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
- smb_fname,
- ×tamp, NULL)) {
+ if (!shadow_copy2_strip_snapshot(talloc_tos(),
+ handle,
+ fsp->fsp_name,
+ ×tamp,
+ NULL)) {
return -1;
}
if (timestamp != 0) {
errno = EROFS;
return -1;
}
- return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
+ return SMB_VFS_NEXT_FNTIMES(handle, fsp, ft);
}
static int shadow_copy2_readlinkat(vfs_handle_struct *handle,
- files_struct *dirfsp,
+ const struct files_struct *dirfsp,
const struct smb_filename *smb_fname,
char *buf,
size_t bufsiz)
char *stripped = NULL;
int saved_errno = 0;
int ret;
+ struct smb_filename *full_fname = NULL;
struct smb_filename *conv = NULL;
- if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
- smb_fname,
- ×tamp, &stripped)) {
+ full_fname = full_path_from_dirfsp_atname(talloc_tos(),
+ dirfsp,
+ smb_fname);
+ if (full_fname == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (!shadow_copy2_strip_snapshot(talloc_tos(),
+ handle,
+ full_fname,
+ ×tamp,
+ &stripped)) {
+ TALLOC_FREE(full_fname);
return -1;
}
+
if (timestamp == 0) {
+ TALLOC_FREE(full_fname);
+ TALLOC_FREE(stripped);
return SMB_VFS_NEXT_READLINKAT(handle,
dirfsp,
smb_fname,
buf,
bufsiz);
}
- conv = cp_smb_filename(talloc_tos(), smb_fname);
+ conv = cp_smb_filename(talloc_tos(), full_fname);
if (conv == NULL) {
+ TALLOC_FREE(full_fname);
TALLOC_FREE(stripped);
errno = ENOMEM;
return -1;
}
+ TALLOC_FREE(full_fname);
conv->base_name = shadow_copy2_convert(
conv, handle, stripped, timestamp);
TALLOC_FREE(stripped);
return -1;
}
ret = SMB_VFS_NEXT_READLINKAT(handle,
- dirfsp,
+ handle->conn->cwd_fsp,
conv,
buf,
bufsiz);
* otherwise return NULL.
*/
static char *have_snapdir(struct vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
const char *path)
{
struct smb_filename smb_fname;
SMB_VFS_HANDLE_GET_DATA(handle, priv, struct shadow_copy2_private,
return NULL);
- ZERO_STRUCT(smb_fname);
- smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
- path, priv->config->snapdir);
+ smb_fname = (struct smb_filename) {
+ .base_name = talloc_asprintf(
+ mem_ctx, "%s/%s", path, priv->config->snapdir),
+ };
if (smb_fname.base_name == NULL) {
return NULL;
}
return NULL;
}
-static bool check_access_snapdir(struct vfs_handle_struct *handle,
- const char *path)
+/**
+ * Find the snapshot directory (if any) for the given
+ * filename (which is relative to the share).
+ */
+static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
+ struct vfs_handle_struct *handle,
+ struct smb_filename *smb_fname)
{
- struct smb_filename smb_fname;
- int ret;
- NTSTATUS status;
-
- ZERO_STRUCT(smb_fname);
- smb_fname.base_name = talloc_asprintf(talloc_tos(),
- "%s",
- path);
- if (smb_fname.base_name == NULL) {
- return false;
- }
-
- ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
- if (ret != 0 || !S_ISDIR(smb_fname.st.st_ex_mode)) {
- TALLOC_FREE(smb_fname.base_name);
- return false;
- }
-
- status = smbd_check_access_rights(handle->conn,
- handle->conn->cwd_fsp,
- &smb_fname,
- false,
- SEC_DIR_LIST);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(0,("user does not have list permission "
- "on snapdir %s\n",
- smb_fname.base_name));
- TALLOC_FREE(smb_fname.base_name);
- return false;
- }
- TALLOC_FREE(smb_fname.base_name);
- return true;
-}
-
-/**
- * Find the snapshot directory (if any) for the given
- * filename (which is relative to the share).
- */
-static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
- struct vfs_handle_struct *handle,
- struct smb_filename *smb_fname)
-{
- char *path, *p;
- const char *snapdir;
- struct shadow_copy2_config *config;
- struct shadow_copy2_private *priv;
+ char *path, *p;
+ const char *snapdir;
+ struct shadow_copy2_config *config;
+ struct shadow_copy2_private *priv;
SMB_VFS_HANDLE_GET_DATA(handle, priv, struct shadow_copy2_private,
return NULL);
config = priv->config;
/*
- * If the non-snapdisrseverywhere mode, we should not search!
+ * If the non-snapdirseverywhere mode, we should not search!
*/
if (!config->snapdirseverywhere) {
return config->snapshot_basepath;
return NULL;
}
- snapdir = have_snapdir(handle, path);
+ snapdir = have_snapdir(handle, talloc_tos(), path);
if (snapdir != NULL) {
TALLOC_FREE(path);
return snapdir;
p[0] = '\0';
- snapdir = have_snapdir(handle, path);
+ snapdir = have_snapdir(handle, talloc_tos(), path);
if (snapdir != NULL) {
TALLOC_FREE(path);
return snapdir;
const char *name,
char *gmt, size_t gmt_len)
{
- struct tm timestamp;
+ struct tm timestamp = { .tm_sec = 0, };
time_t timestamp_t;
unsigned long int timestamp_long;
const char *fmt;
}
}
- ZERO_STRUCT(timestamp);
if (config->use_sscanf) {
if (sscanf(name, fmt, ×tamp_long) != 1) {
DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
}
DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
fmt, name));
-
+
if (config->use_localtime) {
timestamp.tm_isdst = -1;
timestamp_t = mktime(×tamp);
struct shadow_copy2_private *priv = NULL;
struct shadow_copy2_snapentry *tmpentry = NULL;
bool get_snaplist = false;
- bool access_granted = false;
- int open_flags = O_RDONLY;
+ struct vfs_open_how how = {
+ .flags = O_RDONLY, .mode = 0,
+ };
+ int fd;
int ret = -1;
NTSTATUS status;
+ int saved_errno = 0;
snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
if (snapdir == NULL) {
goto done;
}
- access_granted = check_access_snapdir(handle, snapdir);
- if (!access_granted) {
- DEBUG(0,("access denied on listing snapdir %s\n", snapdir));
- errno = EACCES;
- goto done;
- }
-
snapdir_smb_fname = synthetic_smb_fname(talloc_tos(),
snapdir,
NULL,
}
#ifdef O_DIRECTORY
- open_flags |= O_DIRECTORY;
+ how.flags |= O_DIRECTORY;
#endif
- dirfsp->fh->fd = SMB_VFS_NEXT_OPENAT(handle,
- fspcwd,
- snapdir_smb_fname,
- dirfsp,
- open_flags,
- 0);
- if (dirfsp->fh->fd == -1) {
+ fd = SMB_VFS_NEXT_OPENAT(handle,
+ fspcwd,
+ snapdir_smb_fname,
+ dirfsp,
+ &how);
+ if (fd == -1) {
DBG_WARNING("SMB_VFS_NEXT_OPEN failed for '%s'"
" - %s\n", snapdir, strerror(errno));
errno = ENOSYS;
goto done;
}
+ fsp_set_fd(dirfsp, fd);
+
+ /* Now we have the handle, check access here. */
+ status = smbd_check_access_rights_fsp(fspcwd,
+ dirfsp,
+ false,
+ SEC_DIR_LIST);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("user does not have list permission "
+ "on snapdir %s\n",
+ fsp_str_dbg(dirfsp));
+ errno = EACCES;
+ goto done;
+ }
p = SMB_VFS_NEXT_FDOPENDIR(handle, dirfsp, NULL, 0);
if (!p) {
- DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
- " - %s\n", snapdir, strerror(errno)));
+ DBG_NOTICE("shadow_copy2: SMB_VFS_NEXT_FDOPENDIR() failed for '%s'"
+ " - %s\n", snapdir, strerror(errno));
errno = ENOSYS;
goto done;
}
time(&(priv->snaps->fetch_time));
}
- while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
+ while ((d = SMB_VFS_NEXT_READDIR(handle, dirfsp, p))) {
char snapshot[GMT_NAME_LEN+1];
SHADOW_COPY_LABEL *tlabels;
ret = 0;
done:
+ if (ret != 0) {
+ saved_errno = errno;
+ }
TALLOC_FREE(fspcwd );
if (p != NULL) {
SMB_VFS_NEXT_CLOSEDIR(handle, p);
p = NULL;
+ if (dirfsp != NULL) {
+ /*
+ * VFS_CLOSEDIR implicitly
+ * closed the associated fd.
+ */
+ fsp_set_fd(dirfsp, -1);
+ }
}
if (dirfsp != NULL) {
fd_close(dirfsp);
file_free(NULL, dirfsp);
}
TALLOC_FREE(tmp_ctx);
- return ret;
-}
-
-static NTSTATUS shadow_copy2_get_nt_acl_at(vfs_handle_struct *handle,
- struct files_struct *dirfsp,
- const struct smb_filename *smb_fname,
- uint32_t security_info,
- TALLOC_CTX *mem_ctx,
- struct security_descriptor **ppdesc)
-{
- time_t timestamp = 0;
- char *stripped = NULL;
- NTSTATUS status;
- char *conv;
- struct smb_filename *conv_smb_fname = NULL;
-
- if (!shadow_copy2_strip_snapshot(talloc_tos(),
- handle,
- smb_fname,
- ×tamp,
- &stripped)) {
- return map_nt_error_from_unix(errno);
- }
- if (timestamp == 0) {
- return SMB_VFS_NEXT_GET_NT_ACL_AT(handle,
- dirfsp,
- smb_fname,
- security_info,
- 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);
- }
- conv_smb_fname = synthetic_smb_fname(talloc_tos(),
- conv,
- NULL,
- NULL,
- 0,
- smb_fname->flags);
- if (conv_smb_fname == NULL) {
- TALLOC_FREE(conv);
- return NT_STATUS_NO_MEMORY;
+ if (saved_errno != 0) {
+ errno = saved_errno;
}
- status = SMB_VFS_NEXT_GET_NT_ACL_AT(handle,
- dirfsp,
- conv_smb_fname,
- security_info,
- mem_ctx,
- ppdesc);
- TALLOC_FREE(conv);
- TALLOC_FREE(conv_smb_fname);
- return status;
+ return ret;
}
static int shadow_copy2_mkdirat(vfs_handle_struct *handle,
const struct smb_filename *smb_fname,
mode_t mode)
{
+ struct smb_filename *full_fname = NULL;
time_t timestamp = 0;
+ full_fname = full_path_from_dirfsp_atname(talloc_tos(),
+ dirfsp,
+ smb_fname);
+ if (full_fname == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
if (!shadow_copy2_strip_snapshot(talloc_tos(),
handle,
- smb_fname,
+ full_fname,
×tamp,
NULL)) {
+ TALLOC_FREE(full_fname);
return -1;
}
+ TALLOC_FREE(full_fname);
if (timestamp != 0) {
errno = EROFS;
return -1;
mode);
}
-static int shadow_copy2_chflags(vfs_handle_struct *handle,
- const struct smb_filename *smb_fname,
+static int shadow_copy2_fchflags(vfs_handle_struct *handle,
+ struct files_struct *fsp,
unsigned int flags)
{
time_t timestamp = 0;
if (!shadow_copy2_strip_snapshot(talloc_tos(),
handle,
- smb_fname,
+ fsp->fsp_name,
×tamp,
NULL)) {
return -1;
errno = EROFS;
return -1;
}
- return SMB_VFS_NEXT_CHFLAGS(handle, smb_fname, flags);
-}
-
-static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
- const struct smb_filename *smb_fname,
- const char *aname,
- void *value,
- size_t size)
-{
- time_t timestamp = 0;
- char *stripped = NULL;
- ssize_t ret;
- int saved_errno = 0;
- char *conv;
- struct smb_filename *conv_smb_fname = NULL;
-
- if (!shadow_copy2_strip_snapshot(talloc_tos(),
- handle,
- smb_fname,
- ×tamp,
- &stripped)) {
- return -1;
- }
- if (timestamp == 0) {
- return SMB_VFS_NEXT_GETXATTR(handle, smb_fname, aname, value,
- size);
- }
- conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
- TALLOC_FREE(stripped);
- if (conv == NULL) {
- return -1;
- }
-
- conv_smb_fname = synthetic_smb_fname(talloc_tos(),
- conv,
- NULL,
- NULL,
- 0,
- smb_fname->flags);
- if (conv_smb_fname == NULL) {
- TALLOC_FREE(conv);
- return -1;
- }
-
- ret = SMB_VFS_NEXT_GETXATTR(handle, conv_smb_fname, aname, value, size);
- if (ret == -1) {
- saved_errno = errno;
- }
- TALLOC_FREE(conv_smb_fname);
- TALLOC_FREE(conv);
- if (saved_errno != 0) {
- errno = saved_errno;
- }
- return ret;
-}
-
-static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
- const struct smb_filename *smb_fname,
- char *list, size_t size)
-{
- time_t timestamp = 0;
- char *stripped = NULL;
- ssize_t ret;
- int saved_errno = 0;
- char *conv;
- struct smb_filename *conv_smb_fname = NULL;
-
- if (!shadow_copy2_strip_snapshot(talloc_tos(),
- handle,
- smb_fname,
- ×tamp,
- &stripped)) {
- return -1;
- }
- if (timestamp == 0) {
- return SMB_VFS_NEXT_LISTXATTR(handle, smb_fname, list, size);
- }
- conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
- TALLOC_FREE(stripped);
- if (conv == NULL) {
- return -1;
- }
- conv_smb_fname = synthetic_smb_fname(talloc_tos(),
- conv,
- NULL,
- NULL,
- 0,
- smb_fname->flags);
- if (conv_smb_fname == NULL) {
- TALLOC_FREE(conv);
- return -1;
- }
- ret = SMB_VFS_NEXT_LISTXATTR(handle, conv_smb_fname, list, size);
- if (ret == -1) {
- saved_errno = errno;
- }
- TALLOC_FREE(conv_smb_fname);
- TALLOC_FREE(conv);
- if (saved_errno != 0) {
- errno = saved_errno;
- }
- return ret;
-}
-
-static int shadow_copy2_removexattr(vfs_handle_struct *handle,
- const struct smb_filename *smb_fname,
- const char *aname)
-{
- time_t timestamp = 0;
-
- if (!shadow_copy2_strip_snapshot(talloc_tos(),
- handle,
- smb_fname,
- ×tamp,
- NULL)) {
- return -1;
- }
- if (timestamp != 0) {
- errno = EROFS;
- return -1;
- }
- return SMB_VFS_NEXT_REMOVEXATTR(handle, smb_fname, aname);
+ return SMB_VFS_NEXT_FCHFLAGS(handle, fsp, flags);
}
-static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
- const struct smb_filename *smb_fname,
+static int shadow_copy2_fsetxattr(struct vfs_handle_struct *handle,
+ struct files_struct *fsp,
const char *aname, const void *value,
size_t size, int flags)
{
time_t timestamp = 0;
+ const struct smb_filename *smb_fname = NULL;
+ smb_fname = fsp->fsp_name;
if (!shadow_copy2_strip_snapshot(talloc_tos(),
handle,
smb_fname,
errno = EROFS;
return -1;
}
- return SMB_VFS_NEXT_SETXATTR(handle, smb_fname,
+ return SMB_VFS_NEXT_FSETXATTR(handle, fsp,
aname, value, size, flags);
}
static NTSTATUS shadow_copy2_read_dfs_pathat(struct vfs_handle_struct *handle,
TALLOC_CTX *mem_ctx,
struct files_struct *dirfsp,
- const struct smb_filename *smb_fname,
+ struct smb_filename *smb_fname,
struct referral **ppreflist,
size_t *preferral_count)
{
time_t timestamp = 0;
char *stripped = NULL;
+ struct smb_filename *full_fname = NULL;
struct smb_filename *conv = NULL;
NTSTATUS status;
+ full_fname = full_path_from_dirfsp_atname(talloc_tos(),
+ dirfsp,
+ smb_fname);
+ if (full_fname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
if (!shadow_copy2_strip_snapshot(mem_ctx,
handle,
- smb_fname,
+ full_fname,
×tamp,
&stripped)) {
+ TALLOC_FREE(full_fname);
return NT_STATUS_NO_MEMORY;
}
if (timestamp == 0) {
+ TALLOC_FREE(full_fname);
+ TALLOC_FREE(stripped);
return SMB_VFS_NEXT_READ_DFS_PATHAT(handle,
mem_ctx,
dirfsp,
preferral_count);
}
- conv = cp_smb_filename(mem_ctx, smb_fname);
+ conv = cp_smb_filename(mem_ctx, full_fname);
if (conv == NULL) {
+ TALLOC_FREE(full_fname);
TALLOC_FREE(stripped);
return NT_STATUS_NO_MEMORY;
}
+ TALLOC_FREE(full_fname);
conv->base_name = shadow_copy2_convert(conv,
handle,
stripped,
status = SMB_VFS_NEXT_READ_DFS_PATHAT(handle,
mem_ctx,
- dirfsp,
+ handle->conn->cwd_fsp,
conv,
ppreflist,
preferral_count);
- TALLOC_FREE(conv);
- return status;
-}
-
-static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
- const struct smb_filename *fname,
- const char *name,
- TALLOC_CTX *mem_ctx,
- char **found_name)
-{
- struct shadow_copy2_private *priv = NULL;
- struct shadow_copy2_config *config = NULL;
- time_t timestamp = 0;
- char *stripped = NULL;
- ssize_t ret;
- int saved_errno = 0;
- char *conv;
- struct smb_filename conv_fname;
-
- SMB_VFS_HANDLE_GET_DATA(handle, priv, struct shadow_copy2_private,
- return -1);
- config = priv->config;
-
- DBG_DEBUG("Path=[%s] name=[%s]\n", smb_fname_str_dbg(fname), name);
-
- if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
- ×tamp, &stripped)) {
- DEBUG(10, ("shadow_copy2_strip_snapshot failed\n"));
- return -1;
- }
- if (timestamp == 0) {
- DEBUG(10, ("timestamp == 0\n"));
- return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, fname, name,
- mem_ctx, found_name);
+ if (NT_STATUS_IS_OK(status)) {
+ /* Return any stat(2) info. */
+ smb_fname->st = conv->st;
}
- /*
- * Note that stripped may be an empty string "" if path was ".". As
- * shadow_copy2_convert() combines "" with the shadow-copy tree connect
- * root fullpath and get_real_filename_full_scan() has an explicit check
- * for "" this works.
- */
- DBG_DEBUG("stripped [%s]\n", stripped);
-
- conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
- if (conv == NULL) {
- if (!config->snapdirseverywhere) {
- DBG_DEBUG("shadow_copy2_convert [%s] failed\n", stripped);
- return -1;
- }
-
- /*
- * We're called in the path traversal loop in unix_convert()
- * walking down the directory hierarchy. shadow_copy2_convert()
- * will fail if the snapshot directory is futher down in the
- * hierachy. Set conv to the original stripped path and try to
- * look it up in the filesystem with
- * SMB_VFS_NEXT_GET_REAL_FILENAME() or
- * get_real_filename_full_scan().
- */
- DBG_DEBUG("Use stripped [%s] as conv\n", stripped);
- conv = talloc_strdup(talloc_tos(), stripped);
- if (conv == NULL) {
- TALLOC_FREE(stripped);
- return -1;
- }
- }
-
- conv_fname = (struct smb_filename) {
- .base_name = conv,
- };
-
- DEBUG(10, ("Calling NEXT_GET_REAL_FILE_NAME for conv=[%s], "
- "name=[%s]\n", conv, name));
- ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, &conv_fname, name,
- mem_ctx, found_name);
- DEBUG(10, ("NEXT_REAL_FILE_NAME returned %d\n", (int)ret));
- if (ret == 0) {
- return 0;
- }
- if (errno != EOPNOTSUPP) {
- TALLOC_FREE(conv);
- errno = EOPNOTSUPP;
- return -1;
- }
-
- ret = get_real_filename_full_scan(handle->conn,
- conv,
- name,
- false,
- mem_ctx,
- found_name);
- if (ret != 0) {
- saved_errno = errno;
- DBG_DEBUG("Scan [%s] for [%s] failed\n",
- conv, name);
- errno = saved_errno;
- return -1;
- }
-
- DBG_DEBUG("Scan [%s] for [%s] returned [%s]\n",
- conv, name, *found_name);
-
TALLOC_FREE(conv);
- return 0;
+ return status;
}
-static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
- const struct smb_filename *smb_fname_in)
+static const char *shadow_copy2_connectpath(
+ struct vfs_handle_struct *handle,
+ const struct files_struct *dirfsp,
+ const struct smb_filename *smb_fname_in)
{
time_t timestamp = 0;
char *stripped = NULL;
char *tmp = NULL;
const char *fname = smb_fname_in->base_name;
+ const struct smb_filename *full = NULL;
struct smb_filename smb_fname = {0};
struct smb_filename *result_fname = NULL;
char *result = NULL;
return priv->shadow_connectpath;
}
- if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, smb_fname_in,
+ full = full_path_from_dirfsp_atname(
+ talloc_tos(), dirfsp, smb_fname_in);
+ if (full == NULL) {
+ return NULL;
+ }
+
+ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, full,
×tamp, &stripped)) {
goto done;
}
if (timestamp == 0) {
- return SMB_VFS_NEXT_CONNECTPATH(handle, smb_fname_in);
+ return SMB_VFS_NEXT_CONNECTPATH(handle, dirfsp, smb_fname_in);
}
tmp = shadow_copy2_do_convert(talloc_tos(), handle, stripped, timestamp,
return result;
}
+static NTSTATUS shadow_copy2_parent_pathname(vfs_handle_struct *handle,
+ TALLOC_CTX *ctx,
+ const struct smb_filename *smb_fname_in,
+ struct smb_filename **parent_dir_out,
+ struct smb_filename **atname_out)
+{
+ time_t timestamp = 0;
+ char *stripped = NULL;
+ char *converted_name = NULL;
+ struct smb_filename *smb_fname = NULL;
+ struct smb_filename *parent = NULL;
+ struct smb_filename *atname = NULL;
+ struct shadow_copy2_private *priv = NULL;
+ bool ok = false;
+ bool is_converted = false;
+ NTSTATUS status = NT_STATUS_OK;
+ TALLOC_CTX *frame = NULL;
+
+ SMB_VFS_HANDLE_GET_DATA(handle,
+ priv,
+ struct shadow_copy2_private,
+ return NT_STATUS_INTERNAL_ERROR);
+
+ frame = talloc_stackframe();
+
+ smb_fname = cp_smb_filename(frame, smb_fname_in);
+ if (smb_fname == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ /* First, call the default PARENT_PATHNAME. */
+ status = SMB_VFS_NEXT_PARENT_PATHNAME(handle,
+ frame,
+ smb_fname,
+ &parent,
+ &atname);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ if (parent->twrp == 0) {
+ /*
+ * Parent is not a snapshot path, return
+ * the regular result.
+ */
+ status = NT_STATUS_OK;
+ goto out;
+ }
+
+ /* See if we can find a snapshot for the parent. */
+ ok = shadow_copy2_strip_snapshot_converted(frame,
+ handle,
+ parent,
+ ×tamp,
+ &stripped,
+ &is_converted);
+ if (!ok) {
+ status = map_nt_error_from_unix(errno);
+ goto fail;
+ }
+
+ if (is_converted) {
+ /*
+ * Already found snapshot for parent so wipe
+ * out the twrp.
+ */
+ parent->twrp = 0;
+ goto out;
+ }
+
+ converted_name = shadow_copy2_convert(frame,
+ handle,
+ stripped,
+ timestamp);
+
+ if (converted_name == NULL) {
+ /*
+ * Can't find snapshot for parent so wipe
+ * out the twrp.
+ */
+ parent->twrp = 0;
+ }
+
+ out:
+
+ *parent_dir_out = talloc_move(ctx, &parent);
+ if (atname_out != NULL) {
+ *atname_out = talloc_move(*parent_dir_out, &atname);
+ }
+
+ fail:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle,
const struct smb_filename *smb_fname,
uint64_t *bsize,
const char *snapsharepath = NULL;
const char *mount_point;
- DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
- (unsigned)handle->conn->cnum,
- handle->conn->connectpath));
+ DBG_DEBUG("cnum[%" PRIu32 "], connectpath[%s]\n",
+ handle->conn->cnum,
+ handle->conn->connectpath);
ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
if (ret < 0) {
"shadow", "mountpoint", NULL);
if (mount_point != NULL) {
if (mount_point[0] != '/') {
- DEBUG(1, (__location__ " Warning: 'mountpoint' is "
- "relative ('%s'), but it has to be an "
- "absolute path. Ignoring provided value.\n",
- mount_point));
+ DBG_WARNING("Warning: 'mountpoint' is relative "
+ "('%s'), but it has to be an absolute "
+ "path. Ignoring provided value.\n",
+ mount_point);
mount_point = NULL;
} else {
char *p;
if (mount_point != NULL) {
config->mount_point = talloc_strdup(config, mount_point);
if (config->mount_point == NULL) {
- DEBUG(0, (__location__ " talloc_strdup() failed\n"));
+ DBG_ERR("talloc_strdup() failed\n");
return -1;
}
} else {
if (basedir != NULL) {
if (basedir[0] != '/') {
- DEBUG(1, (__location__ " Warning: 'basedir' is "
- "relative ('%s'), but it has to be an "
- "absolute path. Disabling basedir.\n",
- basedir));
+ DBG_WARNING("Warning: 'basedir' is "
+ "relative ('%s'), but it has to be an "
+ "absolute path. Disabling basedir.\n",
+ basedir);
basedir = NULL;
} else {
char *p;
}
if (config->snapdirseverywhere && basedir != NULL) {
- DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
- "with 'snapdirseverywhere'. Disabling basedir.\n"));
+ DBG_WARNING("Warning: 'basedir' is incompatible "
+ "with 'snapdirseverywhere'. Disabling basedir.\n");
basedir = NULL;
}
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"));
+ if (config->snapdirseverywhere) {
+ DBG_WARNING("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"));
+ if (config->crossmountpoints) {
+ DBG_WARNING("Warning: 'crossmountpoints' is not "
+ "supported with an absolute snapdir. "
+ "Disabling it.\n");
config->crossmountpoints = false;
}
.stat_fn = shadow_copy2_stat,
.lstat_fn = shadow_copy2_lstat,
.fstat_fn = shadow_copy2_fstat,
+ .fstatat_fn = shadow_copy2_fstatat,
.openat_fn = shadow_copy2_openat,
.unlinkat_fn = shadow_copy2_unlinkat,
- .chmod_fn = shadow_copy2_chmod,
+ .fchmod_fn = shadow_copy2_fchmod,
.chdir_fn = shadow_copy2_chdir,
- .ntimes_fn = shadow_copy2_ntimes,
+ .fntimes_fn = shadow_copy2_fntimes,
.readlinkat_fn = shadow_copy2_readlinkat,
.mknodat_fn = shadow_copy2_mknodat,
.realpath_fn = shadow_copy2_realpath,
- .get_nt_acl_at_fn = shadow_copy2_get_nt_acl_at,
.get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
.mkdirat_fn = shadow_copy2_mkdirat,
- .getxattr_fn = shadow_copy2_getxattr,
- .getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
- .getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
- .listxattr_fn = shadow_copy2_listxattr,
- .removexattr_fn = shadow_copy2_removexattr,
- .setxattr_fn = shadow_copy2_setxattr,
- .chflags_fn = shadow_copy2_chflags,
- .get_real_filename_fn = shadow_copy2_get_real_filename,
+ .fsetxattr_fn = shadow_copy2_fsetxattr,
+ .fchflags_fn = shadow_copy2_fchflags,
.pwrite_fn = shadow_copy2_pwrite,
.pwrite_send_fn = shadow_copy2_pwrite_send,
.pwrite_recv_fn = shadow_copy2_pwrite_recv,
.connectpath_fn = shadow_copy2_connectpath,
+ .parent_pathname_fn = shadow_copy2_parent_pathname,
};
static_decl_vfs;