X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=source3%2Fmodules%2Fonefs_streams.c;h=c22fc711a0e74093a31a652d6da1e3c1081dd6a3;hb=258952aa85f2a68e2d2362522f6114c6a439f1e3;hp=615edf379d34fc140942ca3f2e8647d9d539e5d0;hpb=9b366d703210b493aa1389bbdd288a2b00958766;p=nivanova%2Fsamba-autobuild%2F.git diff --git a/source3/modules/onefs_streams.c b/source3/modules/onefs_streams.c index 615edf379d3..c22fc711a0e 100644 --- a/source3/modules/onefs_streams.c +++ b/source3/modules/onefs_streams.c @@ -19,7 +19,10 @@ * along with this program; if not, see . */ +#include "includes.h" #include "onefs.h" +#include "onefs_config.h" + #include /* @@ -51,6 +54,52 @@ NTSTATUS onefs_split_ntfs_stream_name(TALLOC_CTX *mem_ctx, const char *fname, return NT_STATUS_OK; } +NTSTATUS onefs_stream_prep_smb_fname(TALLOC_CTX *ctx, + const struct smb_filename *smb_fname_in, + struct smb_filename **smb_fname_out) +{ + char *stream_name = NULL; + NTSTATUS status; + + /* + * Only attempt to strip off the trailing :$DATA if there is an actual + * stream there. If it is the default stream, the smb_fname_out will + * just have a NULL stream so the base file is opened. + */ + if (smb_fname_in->stream_name && + !is_ntfs_default_stream_smb_fname(smb_fname_in)) { + char *str_tmp = smb_fname_in->stream_name; + + /* First strip off the leading ':' */ + if (str_tmp[0] == ':') { + str_tmp++; + } + + /* Create a new copy of the stream_name. */ + stream_name = talloc_strdup(ctx, str_tmp); + if (stream_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + + /* Strip off the :$DATA if one exists. */ + str_tmp = strrchr_m(stream_name, ':'); + if (str_tmp) { + str_tmp[0] = '\0'; + } + } + + /* + * If there was a stream that wasn't the default stream the leading + * colon and trailing :$DATA has now been stripped off. Create a new + * smb_filename to pass back. + */ + status = create_synthetic_smb_fname(ctx, smb_fname_in->base_name, + stream_name, &smb_fname_in->st, + smb_fname_out); + TALLOC_FREE(stream_name); + return status; +} + int onefs_is_stream(const char *path, char **pbase, char **pstream, bool *is_stream) { @@ -146,58 +195,80 @@ static int get_stream_dir_fd(connection_struct *conn, const char *base, return dir_fd; } -int onefs_rename(vfs_handle_struct *handle, const char *oldname, - const char *newname) +int onefs_rename(vfs_handle_struct *handle, + const struct smb_filename *smb_fname_src, + const struct smb_filename *smb_fname_dst) { - TALLOC_CTX *frame = NULL; - int ret = -1; - int dir_fd = -1; + struct smb_filename *smb_fname_src_onefs = NULL; + struct smb_filename *smb_fname_dst_onefs = NULL; + NTSTATUS status; int saved_errno; - bool old_is_stream; - bool new_is_stream; - char *obase = NULL; - char *osname = NULL; - char *nbase = NULL; - char *nsname = NULL; + int dir_fd = -1; + int ret = -1; - frame = talloc_stackframe(); + START_PROFILE(syscall_rename_at); - ret = onefs_is_stream(oldname, &obase, &osname, &old_is_stream); - if (ret) - return ret; + if (!is_ntfs_stream_smb_fname(smb_fname_src) && + !is_ntfs_stream_smb_fname(smb_fname_dst)) { + ret = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, + smb_fname_dst); + goto done; + } - ret = onefs_is_stream(newname, &nbase, &nsname, &new_is_stream); - if (ret) - return ret; + /* For now don't allow renames from or to the default stream. */ + if (is_ntfs_default_stream_smb_fname(smb_fname_src) || + is_ntfs_default_stream_smb_fname(smb_fname_dst)) { + errno = ENOSYS; + goto done; + } - if (!old_is_stream && !new_is_stream) { - return SMB_VFS_NEXT_RENAME(handle, oldname, newname); + /* prep stream smb_filename structs. */ + status = onefs_stream_prep_smb_fname(talloc_tos(), smb_fname_src, + &smb_fname_src_onefs); + if (!NT_STATUS_IS_OK(status)) { + errno = map_errno_from_nt_status(status); + goto done; + } + status = onefs_stream_prep_smb_fname(talloc_tos(), smb_fname_dst, + &smb_fname_dst_onefs); + if (!NT_STATUS_IS_OK(status)) { + errno = map_errno_from_nt_status(status); + goto done; } - dir_fd = get_stream_dir_fd(handle->conn, obase, NULL); + dir_fd = get_stream_dir_fd(handle->conn, smb_fname_src->base_name, + NULL); if (dir_fd < -1) { goto done; } - DEBUG(8,("onefs_rename called for %s : %s => %s : %s\n", - obase, osname, nbase, nsname)); + DEBUG(8, ("onefs_rename called for %s => %s\n", + smb_fname_str_dbg(smb_fname_src_onefs), + smb_fname_str_dbg(smb_fname_dst_onefs))); /* Handle rename of stream to default stream specially. */ - if (nsname == NULL) { - ret = enc_renameat(dir_fd, osname, ENC_DEFAULT, AT_FDCWD, - nbase, ENC_DEFAULT); + if (smb_fname_dst_onefs->stream_name == NULL) { + ret = enc_renameat(dir_fd, smb_fname_src_onefs->stream_name, + ENC_DEFAULT, AT_FDCWD, + smb_fname_dst_onefs->base_name, + ENC_DEFAULT); } else { - ret = enc_renameat(dir_fd, osname, ENC_DEFAULT, dir_fd, nsname, + ret = enc_renameat(dir_fd, smb_fname_src_onefs->stream_name, + ENC_DEFAULT, dir_fd, + smb_fname_dst_onefs->stream_name, ENC_DEFAULT); } done: + END_PROFILE(syscall_rename_at); + TALLOC_FREE(smb_fname_src_onefs); + TALLOC_FREE(smb_fname_dst_onefs); + saved_errno = errno; if (dir_fd >= 0) { close(dir_fd); } errno = saved_errno; - TALLOC_FREE(frame); return ret; } @@ -209,81 +280,84 @@ static void merge_stat(SMB_STRUCT_STAT *stream_sbuf, { int dos_flags = (UF_DOS_NOINDEX | UF_DOS_ARCHIVE | UF_DOS_HIDDEN | UF_DOS_RO | UF_DOS_SYSTEM); - stream_sbuf->st_mtime = base_sbuf->st_mtime; - stream_sbuf->st_ctime = base_sbuf->st_ctime; - stream_sbuf->st_atime = base_sbuf->st_atime; - stream_sbuf->st_flags &= ~dos_flags; - stream_sbuf->st_flags |= base_sbuf->st_flags & dos_flags; + stream_sbuf->st_ex_mtime = base_sbuf->st_ex_mtime; + stream_sbuf->st_ex_ctime = base_sbuf->st_ex_ctime; + stream_sbuf->st_ex_atime = base_sbuf->st_ex_atime; + stream_sbuf->st_ex_flags &= ~dos_flags; + stream_sbuf->st_ex_flags |= base_sbuf->st_ex_flags & dos_flags; } /* fake timestamps */ -static void onefs_adjust_stat_time(vfs_handle_struct *handle, const char *fname, - SMB_STRUCT_STAT *sbuf) +static void onefs_adjust_stat_time(struct connection_struct *conn, + const char *fname, SMB_STRUCT_STAT *sbuf) { - struct onefs_vfs_config cfg; + struct onefs_vfs_share_config cfg; struct timeval tv_now = {0, 0}; bool static_mtime = False; bool static_atime = False; - if (!onefs_get_config(SNUM(handle->conn), + if (!onefs_get_config(SNUM(conn), ONEFS_VFS_CONFIG_FAKETIMESTAMPS, &cfg)) { return; } - if (IS_MTIME_STATIC_PATH(handle->conn, &cfg, fname)) { - sbuf->st_mtime = sbuf->st_birthtime; + if (IS_MTIME_STATIC_PATH(conn, &cfg, fname)) { + sbuf->st_ex_mtime = sbuf->st_ex_btime; static_mtime = True; } - if (IS_ATIME_STATIC_PATH(handle->conn, &cfg, fname)) { - sbuf->st_atime = sbuf->st_birthtime; + if (IS_ATIME_STATIC_PATH(conn, &cfg, fname)) { + sbuf->st_ex_atime = sbuf->st_ex_btime; static_atime = True; } - if (IS_CTIME_NOW_PATH(handle->conn, &cfg, fname)) { + if (IS_CTIME_NOW_PATH(conn, &cfg, fname)) { if (cfg.ctime_slop < 0) { - sbuf->st_birthtime = INT_MAX - 1; + sbuf->st_ex_btime.tv_sec = INT_MAX - 1; } else { GetTimeOfDay(&tv_now); - sbuf->st_birthtime = tv_now.tv_sec + cfg.ctime_slop; + sbuf->st_ex_btime.tv_sec = tv_now.tv_sec + + cfg.ctime_slop; } } - if (!static_mtime && IS_MTIME_NOW_PATH(handle->conn,&cfg,fname)) { + if (!static_mtime && IS_MTIME_NOW_PATH(conn,&cfg,fname)) { if (cfg.mtime_slop < 0) { - sbuf->st_mtime = INT_MAX - 1; + sbuf->st_ex_mtime.tv_sec = INT_MAX - 1; } else { if (tv_now.tv_sec == 0) GetTimeOfDay(&tv_now); - sbuf->st_mtime = tv_now.tv_sec + cfg.mtime_slop; + sbuf->st_ex_mtime.tv_sec = tv_now.tv_sec + + cfg.mtime_slop; } } - if (!static_atime && IS_ATIME_NOW_PATH(handle->conn,&cfg,fname)) { + if (!static_atime && IS_ATIME_NOW_PATH(conn,&cfg,fname)) { if (cfg.atime_slop < 0) { - sbuf->st_atime = INT_MAX - 1; + sbuf->st_ex_atime.tv_sec = INT_MAX - 1; } else { if (tv_now.tv_sec == 0) GetTimeOfDay(&tv_now); - sbuf->st_atime = tv_now.tv_sec + cfg.atime_slop; + sbuf->st_ex_atime.tv_sec = tv_now.tv_sec + + cfg.atime_slop; } } } -static int stat_stream(vfs_handle_struct *handle, const char *base, +static int stat_stream(struct connection_struct *conn, const char *base, const char *stream, SMB_STRUCT_STAT *sbuf, int flags) { SMB_STRUCT_STAT base_sbuf; int base_fd = -1, dir_fd, ret, saved_errno; - dir_fd = get_stream_dir_fd(handle->conn, base, &base_fd); + dir_fd = get_stream_dir_fd(conn, base, &base_fd); if (dir_fd < 0) { return -1; } /* Stat the stream. */ - ret = enc_fstatat(dir_fd, stream, ENC_DEFAULT, sbuf, flags); + ret = onefs_sys_fstat_at(dir_fd, stream, sbuf, flags); if (ret != -1) { /* Now stat the base file and merge the results. */ - ret = sys_fstat(base_fd, &base_sbuf); + ret = onefs_sys_fstat(base_fd, &base_sbuf); if (ret != -1) { merge_stat(sbuf, &base_sbuf); } @@ -296,28 +370,37 @@ static int stat_stream(vfs_handle_struct *handle, const char *base, return ret; } -int onefs_stat(vfs_handle_struct *handle, const char *path, - SMB_STRUCT_STAT *sbuf) +int onefs_stat(vfs_handle_struct *handle, struct smb_filename *smb_fname) { + struct smb_filename *smb_fname_onefs = NULL; + NTSTATUS status; int ret; - bool is_stream; - char *base = NULL; - char *stream = NULL; - ret = onefs_is_stream(path, &base, &stream, &is_stream); - if (ret) - return ret; + status = onefs_stream_prep_smb_fname(talloc_tos(), smb_fname, + &smb_fname_onefs); + if (!NT_STATUS_IS_OK(status)) { + errno = map_errno_from_nt_status(status); + return -1; + } - if (!is_stream) { - ret = SMB_VFS_NEXT_STAT(handle, path, sbuf); - } else if (!stream) { - /* If it's the ::$DATA stream just stat the base file name. */ - ret = SMB_VFS_NEXT_STAT(handle, base, sbuf); + /* + * If the smb_fname has no stream or is :$DATA, then just stat the + * base stream. Otherwise stat the stream. + */ + if (!is_ntfs_stream_smb_fname(smb_fname_onefs)) { + ret = onefs_sys_stat(smb_fname_onefs->base_name, + &smb_fname->st); } else { - ret = stat_stream(handle, base, stream, sbuf, 0); + ret = stat_stream(handle->conn, smb_fname_onefs->base_name, + smb_fname_onefs->stream_name, &smb_fname->st, + 0); } - onefs_adjust_stat_time(handle, path, sbuf); + onefs_adjust_stat_time(handle->conn, smb_fname->base_name, + &smb_fname->st); + + TALLOC_FREE(smb_fname_onefs); + return ret; } @@ -328,81 +411,100 @@ int onefs_fstat(vfs_handle_struct *handle, struct files_struct *fsp, int ret; /* Stat the stream, by calling next_fstat on the stream's fd. */ - ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf); + ret = onefs_sys_fstat(fsp->fh->fd, sbuf); if (ret == -1) { return ret; } /* Stat the base file and merge the results. */ if (fsp != NULL && fsp->base_fsp != NULL) { - ret = sys_fstat(fsp->base_fsp->fh->fd, &base_sbuf); + ret = onefs_sys_fstat(fsp->base_fsp->fh->fd, &base_sbuf); if (ret != -1) { merge_stat(sbuf, &base_sbuf); } } - onefs_adjust_stat_time(handle, fsp->fsp_name, sbuf); + onefs_adjust_stat_time(handle->conn, fsp->fsp_name, sbuf); return ret; } -int onefs_lstat(vfs_handle_struct *handle, const char *path, - SMB_STRUCT_STAT *sbuf) +int onefs_lstat(vfs_handle_struct *handle, struct smb_filename *smb_fname) { + struct smb_filename *smb_fname_onefs = NULL; + NTSTATUS status; int ret; - bool is_stream; - char *base = NULL; - char *stream = NULL; - ret = onefs_is_stream(path, &base, &stream, &is_stream); - if (ret) - return ret; + status = onefs_stream_prep_smb_fname(talloc_tos(), smb_fname, + &smb_fname_onefs); + if (!NT_STATUS_IS_OK(status)) { + errno = map_errno_from_nt_status(status); + return -1; + } - if (!is_stream) { - ret = SMB_VFS_NEXT_LSTAT(handle, path, sbuf); - } else if (!stream) { - /* If it's the ::$DATA stream just stat the base file name. */ - ret = SMB_VFS_NEXT_LSTAT(handle, base, sbuf); + /* + * If the smb_fname has no stream or is :$DATA, then just stat the + * base stream. Otherwise stat the stream. + */ + if (!is_ntfs_stream_smb_fname(smb_fname_onefs)) { + ret = onefs_sys_lstat(smb_fname_onefs->base_name, + &smb_fname->st); } else { - ret = stat_stream(handle, base, stream, sbuf, + ret = stat_stream(handle->conn, smb_fname_onefs->base_name, + smb_fname_onefs->stream_name, &smb_fname->st, AT_SYMLINK_NOFOLLOW); } - onefs_adjust_stat_time(handle, path, sbuf); + onefs_adjust_stat_time(handle->conn, smb_fname->base_name, + &smb_fname->st); + + TALLOC_FREE(smb_fname_onefs); + return ret; } -int onefs_unlink(vfs_handle_struct *handle, const char *path) +int onefs_unlink(vfs_handle_struct *handle, + const struct smb_filename *smb_fname) { + struct smb_filename *smb_fname_onefs = NULL; int ret; bool is_stream; char *base = NULL; char *stream = NULL; int dir_fd, saved_errno; - ret = onefs_is_stream(path, &base, &stream, &is_stream); - if (ret) { - return ret; + /* Not a stream. */ + if (!is_ntfs_stream_smb_fname(smb_fname)) { + return SMB_VFS_NEXT_UNLINK(handle, smb_fname); } - if (!is_stream) { - return SMB_VFS_NEXT_UNLINK(handle, path); + status = onefs_stream_prep_smb_fname(talloc_tos(), smb_fname, + &smb_fname_onefs); + if (!NT_STATUS_IS_OK(status)) { + errno = map_errno_from_nt_status(status); + return -1; } - /* If it's the ::$DATA stream just unlink the base file name. */ - if (!stream) { - return SMB_VFS_NEXT_UNLINK(handle, base); + /* Default stream (the ::$DATA was just stripped off). */ + if (!is_ntfs_stream_smb_fname(smb_fname_onefs)) { + ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname_onefs); + goto out; } - dir_fd = get_stream_dir_fd(handle->conn, base, NULL); + dir_fd = get_stream_dir_fd(handle->conn, smb_fname_onefs->base_name, + NULL); if (dir_fd < 0) { - return -1; + ret = -1; + goto out; } - ret = enc_unlinkat(dir_fd, stream, ENC_DEFAULT, 0); + ret = enc_unlinkat(dir_fd, smb_fname_onefs->stream_name, ENC_DEFAULT, + 0); saved_errno = errno; close(dir_fd); errno = saved_errno; + out: + TALLOC_FREE(smb_fname_onefs); return ret; } @@ -540,12 +642,25 @@ static NTSTATUS walk_onefs_streams(connection_struct *conn, files_struct *fsp, goto out; } + /* Initialize the dir state struct and add it to the list. + * This is a layer violation, and really should be handled by a + * VFS_FDOPENDIR() call which would properly setup the dir state. + * But since this is all within the onefs.so module, we cheat for + * now and call directly into the readdirplus code. + * NOTE: This state MUST be freed by a proper VFS_CLOSEDIR() call. */ + ret = onefs_rdp_add_dir_state(conn, dirp); + if (ret) { + DEBUG(0, ("Error adding dir_state to the list\n")); + status = map_nt_error_from_unix(errno); + goto out; + } + fake_fs.conn = conn; fake_fs.fh = &fake_fh; fake_fs.fsp_name = SMB_STRDUP(fname); /* Iterate over the streams in the ADS directory. */ - while ((dp = SMB_VFS_READDIR(conn, dirp)) != NULL) { + while ((dp = SMB_VFS_READDIR(conn, dirp, NULL)) != NULL) { /* Skip the "." and ".." entries */ if ((strcmp(dp->d_name, ".") == 0) || (strcmp(dp->d_name, "..") == 0)) @@ -588,7 +703,7 @@ static NTSTATUS walk_onefs_streams(connection_struct *conn, files_struct *fsp, if (!add_one_stream(state->mem_ctx, &state->num_streams, &state->streams, - dp->d_name, stream_sbuf.st_size, + dp->d_name, stream_sbuf.st_ex_size, SMB_VFS_GET_ALLOC_SIZE(conn, NULL, &stream_sbuf))) { state->status = NT_STATUS_NO_MEMORY; @@ -632,10 +747,22 @@ NTSTATUS onefs_streaminfo(vfs_handle_struct *handle, } ret = SMB_VFS_FSTAT(fsp, &sbuf); } else { + struct smb_filename *smb_fname = NULL; + if (is_ntfs_stream_name(fname)) { return NT_STATUS_INVALID_PARAMETER; } - ret = SMB_VFS_STAT(handle->conn, fname, &sbuf); + + status = create_synthetic_smb_fname(talloc_tos(), fname, NULL, + NULL, &smb_fname); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + ret = SMB_VFS_STAT(handle->conn, smb_fname); + + sbuf = smb_fname->st; + + TALLOC_FREE(smb_fname); } if (ret == -1) { @@ -645,11 +772,16 @@ NTSTATUS onefs_streaminfo(vfs_handle_struct *handle, state.streams = NULL; state.num_streams = 0; + if (lp_parm_bool(SNUM(handle->conn), PARM_ONEFS_TYPE, + PARM_IGNORE_STREAMS, PARM_IGNORE_STREAMS_DEFAULT)) { + goto out; + } + /* Add the default stream. */ - if (S_ISREG(sbuf.st_mode)) { + if (S_ISREG(sbuf.st_ex_mode)) { if (!add_one_stream(mem_ctx, &state.num_streams, &state.streams, - "", sbuf.st_size, + "", sbuf.st_ex_size, SMB_VFS_GET_ALLOC_SIZE(handle->conn, fsp, &sbuf))) { return NT_STATUS_NO_MEMORY; @@ -661,7 +793,7 @@ NTSTATUS onefs_streaminfo(vfs_handle_struct *handle, state.status = NT_STATUS_OK; /* If there are more streams, add them too. */ - if (sbuf.st_flags & UF_HASADS) { + if (sbuf.st_ex_flags & UF_HASADS) { status = walk_onefs_streams(handle->conn, fsp, fname, &state, &sbuf); @@ -676,7 +808,7 @@ NTSTATUS onefs_streaminfo(vfs_handle_struct *handle, return state.status; } } - + out: *num_streams = state.num_streams; *streams = state.streams; return NT_STATUS_OK;