mirror of
https://github.com/samba-team/samba.git
synced 2024-12-25 23:21:54 +03:00
2163 lines
58 KiB
C
2163 lines
58 KiB
C
/*
|
|
* Unix SMB/CIFS implementation.
|
|
*
|
|
* This file began with some code from source3/smbd/open.c and has been
|
|
* modified it work with ifs_createfile.
|
|
*
|
|
* ifs_createfile is a CIFS-specific syscall for opening/files and
|
|
* directories. It adds support for:
|
|
* - Full in-kernel access checks using a windows access_mask
|
|
* - Cluster-coherent share mode locks
|
|
* - Cluster-coherent oplocks
|
|
* - Streams
|
|
* - Setting security descriptors at create time
|
|
* - Setting dos_attributes at create time
|
|
*
|
|
* Copyright (C) Andrew Tridgell 1992-1998
|
|
* Copyright (C) Jeremy Allison 2001-2004
|
|
* Copyright (C) Volker Lendecke 2005
|
|
* Copyright (C) Tim Prouty, 2008
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "onefs.h"
|
|
#include "onefs_config.h"
|
|
#include "oplock_onefs.h"
|
|
#include "smbd/globals.h"
|
|
|
|
extern const struct generic_mapping file_generic_mapping;
|
|
|
|
struct onefs_fsp_data {
|
|
uint64_t oplock_callback_id;
|
|
};
|
|
|
|
static NTSTATUS onefs_create_file_unixpath(connection_struct *conn,
|
|
struct smb_request *req,
|
|
const char *fname,
|
|
uint32_t access_mask,
|
|
uint32_t share_access,
|
|
uint32_t create_disposition,
|
|
uint32_t create_options,
|
|
uint32_t file_attributes,
|
|
uint32_t oplock_request,
|
|
uint64_t allocation_size,
|
|
struct security_descriptor *sd,
|
|
struct ea_list *ea_list,
|
|
files_struct **result,
|
|
int *pinfo,
|
|
struct onefs_fsp_data *fsp_data,
|
|
SMB_STRUCT_STAT *psbuf);
|
|
|
|
/****************************************************************************
|
|
Open a file.
|
|
****************************************************************************/
|
|
|
|
static NTSTATUS onefs_open_file(files_struct *fsp,
|
|
connection_struct *conn,
|
|
struct smb_request *req,
|
|
const char *parent_dir,
|
|
const char *name,
|
|
const char *path,
|
|
SMB_STRUCT_STAT *psbuf,
|
|
int flags,
|
|
mode_t unx_mode,
|
|
uint32 access_mask,
|
|
uint32 open_access_mask,
|
|
int oplock_request,
|
|
uint64 id,
|
|
uint32 share_access,
|
|
uint32 create_options,
|
|
uint32_t new_dos_attributes,
|
|
struct security_descriptor *sd,
|
|
int *granted_oplock)
|
|
{
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
int accmode = (flags & O_ACCMODE);
|
|
int local_flags = flags;
|
|
bool file_existed = VALID_STAT(*psbuf);
|
|
const char *wild;
|
|
char *base = NULL;
|
|
char *stream = NULL;
|
|
int base_fd = -1;
|
|
|
|
fsp->fh->fd = -1;
|
|
errno = EPERM;
|
|
|
|
/* Check permissions */
|
|
|
|
/*
|
|
* This code was changed after seeing a client open request
|
|
* containing the open mode of (DENY_WRITE/read-only) with
|
|
* the 'create if not exist' bit set. The previous code
|
|
* would fail to open the file read only on a read-only share
|
|
* as it was checking the flags parameter directly against O_RDONLY,
|
|
* this was failing as the flags parameter was set to O_RDONLY|O_CREAT.
|
|
* JRA.
|
|
*/
|
|
|
|
if (!CAN_WRITE(conn)) {
|
|
/* It's a read-only share - fail if we wanted to write. */
|
|
if(accmode != O_RDONLY) {
|
|
DEBUG(3,("Permission denied opening %s\n", path));
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
} else if(flags & O_CREAT) {
|
|
/* We don't want to write - but we must make sure that
|
|
O_CREAT doesn't create the file if we have write
|
|
access into the directory.
|
|
*/
|
|
flags &= ~O_CREAT;
|
|
local_flags &= ~O_CREAT;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This little piece of insanity is inspired by the
|
|
* fact that an NT client can open a file for O_RDONLY,
|
|
* but set the create disposition to FILE_EXISTS_TRUNCATE.
|
|
* If the client *can* write to the file, then it expects to
|
|
* truncate the file, even though it is opening for readonly.
|
|
* Quicken uses this stupid trick in backup file creation...
|
|
* Thanks *greatly* to "David W. Chapman Jr." <dwcjr@inethouston.net>
|
|
* for helping track this one down. It didn't bite us in 2.0.x
|
|
* as we always opened files read-write in that release. JRA.
|
|
*/
|
|
|
|
if ((accmode == O_RDONLY) && ((flags & O_TRUNC) == O_TRUNC)) {
|
|
DEBUG(10,("onefs_open_file: truncate requested on read-only "
|
|
"open for file %s\n", path));
|
|
local_flags = (flags & ~O_ACCMODE)|O_RDWR;
|
|
}
|
|
|
|
#if defined(O_NONBLOCK) && defined(S_ISFIFO)
|
|
/*
|
|
* We would block on opening a FIFO with no one else on the
|
|
* other end. Do what we used to do and add O_NONBLOCK to the
|
|
* open flags. JRA.
|
|
*/
|
|
|
|
if (file_existed && S_ISFIFO(psbuf->st_ex_mode)) {
|
|
local_flags |= O_NONBLOCK;
|
|
}
|
|
#endif
|
|
|
|
/* Don't create files with Microsoft wildcard characters. */
|
|
if (fsp->base_fsp) {
|
|
/*
|
|
* wildcard characters are allowed in stream names
|
|
* only test the basefilename
|
|
*/
|
|
wild = fsp->base_fsp->fsp_name;
|
|
} else {
|
|
wild = path;
|
|
}
|
|
if ((local_flags & O_CREAT) && !file_existed &&
|
|
ms_has_wild(wild)) {
|
|
/*
|
|
* XXX: may need to remvoe this return...
|
|
*
|
|
* We dont think this check needs to exist. All it does is
|
|
* block creating files with Microsoft wildcards, which is
|
|
* fine if the creation originated from NFS or locally and
|
|
* then was copied via Samba.
|
|
*/
|
|
DEBUG(1, ("onefs_open_file: creating file with wildcard: %s\n",
|
|
path));
|
|
return NT_STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
|
|
/* Actually do the open */
|
|
|
|
#ifdef O_NOFOLLOW
|
|
/*
|
|
* Never follow symlinks on a POSIX client. The
|
|
* client should be doing this.
|
|
*/
|
|
|
|
if (fsp->posix_open || !lp_symlinks(SNUM(conn))) {
|
|
flags |= O_NOFOLLOW;
|
|
}
|
|
#endif
|
|
/* Stream handling */
|
|
if (is_ntfs_stream_name(path)) {
|
|
status = onefs_split_ntfs_stream_name(talloc_tos(), path,
|
|
&base, &stream);
|
|
}
|
|
/* It's a stream, so pass in the base_fd */
|
|
if ((conn->fs_capabilities & FILE_NAMED_STREAMS) && stream != NULL) {
|
|
SMB_ASSERT(fsp->base_fsp);
|
|
|
|
DEBUG(10,("Opening a stream: base=%s(%d), stream=%s\n",
|
|
base, fsp->base_fsp->fh->fd, stream));
|
|
|
|
base_fd = fsp->base_fsp->fh->fd;
|
|
}
|
|
|
|
fsp->fh->fd = onefs_sys_create_file(conn,
|
|
base_fd,
|
|
stream != NULL ? stream :
|
|
(base != NULL ? base : path),
|
|
access_mask,
|
|
open_access_mask,
|
|
share_access,
|
|
create_options,
|
|
flags,
|
|
unx_mode,
|
|
oplock_request,
|
|
id,
|
|
sd,
|
|
new_dos_attributes,
|
|
granted_oplock);
|
|
|
|
if (fsp->fh->fd == -1) {
|
|
if (errno == EMFILE) {
|
|
static time_t last_warned = 0L;
|
|
|
|
if (time((time_t *) NULL) > last_warned) {
|
|
DEBUG(0, ("Too many open files, unable "
|
|
"to open more! smbd's max "
|
|
"open files = %d, also check "
|
|
"sysctl kern.maxfiles and "
|
|
"sysctl kern.maxfilesperproc\n",
|
|
lp_max_open_files()));
|
|
last_warned = time((time_t *) NULL);
|
|
}
|
|
}
|
|
|
|
status = map_nt_error_from_unix(errno);
|
|
DEBUG(3,("Error opening file %s (%s) (local_flags=%d) "
|
|
"(flags=%d)\n",
|
|
path, strerror(errno), local_flags, flags));
|
|
return status;
|
|
}
|
|
|
|
if ((local_flags & O_CREAT) && !file_existed) {
|
|
|
|
/* Inherit the ACL if required */
|
|
if (lp_inherit_perms(SNUM(conn))) {
|
|
inherit_access_posix_acl(conn, parent_dir, path,
|
|
unx_mode);
|
|
}
|
|
|
|
/* Change the owner if required. */
|
|
if (lp_inherit_owner(SNUM(conn))) {
|
|
change_file_owner_to_parent(conn, parent_dir,
|
|
fsp);
|
|
}
|
|
|
|
notify_fname(conn, NOTIFY_ACTION_ADDED,
|
|
FILE_NOTIFY_CHANGE_FILE_NAME, path);
|
|
}
|
|
|
|
if (!file_existed) {
|
|
int ret;
|
|
|
|
if (fsp->fh->fd == -1) {
|
|
ret = SMB_VFS_STAT(conn, path, psbuf);
|
|
} else {
|
|
ret = SMB_VFS_FSTAT(fsp, psbuf);
|
|
/* If we have an fd, this stat should succeed. */
|
|
if (ret == -1) {
|
|
DEBUG(0,("Error doing fstat on open file %s "
|
|
"(%s)\n", path,strerror(errno) ));
|
|
}
|
|
}
|
|
|
|
/* For a non-io open, this stat failing means file not found. JRA */
|
|
if (ret == -1) {
|
|
status = map_nt_error_from_unix(errno);
|
|
fd_close(fsp);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* POSIX allows read-only opens of directories. We don't
|
|
* want to do this (we use a different code path for this)
|
|
* so catch a directory open and return an EISDIR. JRA.
|
|
*/
|
|
|
|
if(S_ISDIR(psbuf->st_ex_mode)) {
|
|
fd_close(fsp);
|
|
errno = EISDIR;
|
|
return NT_STATUS_FILE_IS_A_DIRECTORY;
|
|
}
|
|
|
|
fsp->mode = psbuf->st_ex_mode;
|
|
fsp->file_id = vfs_file_id_from_sbuf(conn, psbuf);
|
|
fsp->vuid = req ? req->vuid : UID_FIELD_INVALID;
|
|
fsp->file_pid = req ? req->smbpid : 0;
|
|
fsp->can_lock = True;
|
|
fsp->can_read = (access_mask & (FILE_READ_DATA)) ? True : False;
|
|
if (!CAN_WRITE(conn)) {
|
|
fsp->can_write = False;
|
|
} else {
|
|
fsp->can_write = (access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) ?
|
|
True : False;
|
|
}
|
|
fsp->print_file = False;
|
|
fsp->modified = False;
|
|
fsp->sent_oplock_break = NO_BREAK_SENT;
|
|
fsp->is_directory = False;
|
|
if (conn->aio_write_behind_list &&
|
|
is_in_path(path, conn->aio_write_behind_list, conn->case_sensitive)) {
|
|
fsp->aio_write_behind = True;
|
|
}
|
|
|
|
string_set(&fsp->fsp_name, path);
|
|
fsp->wcp = NULL; /* Write cache pointer. */
|
|
|
|
DEBUG(2,("%s opened file %s read=%s write=%s (numopen=%d)\n",
|
|
conn->server_info->unix_name,
|
|
fsp->fsp_name,
|
|
BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write),
|
|
conn->num_files_open));
|
|
|
|
errno = 0;
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Handle the 1 second delay in returning a SHARING_VIOLATION error.
|
|
****************************************************************************/
|
|
|
|
static void defer_open(struct share_mode_lock *lck,
|
|
struct timeval request_time,
|
|
struct timeval timeout,
|
|
struct smb_request *req,
|
|
struct deferred_open_record *state)
|
|
{
|
|
int i;
|
|
|
|
/* Paranoia check */
|
|
|
|
for (i=0; i<lck->num_share_modes; i++) {
|
|
struct share_mode_entry *e = &lck->share_modes[i];
|
|
|
|
if (!is_deferred_open_entry(e)) {
|
|
continue;
|
|
}
|
|
|
|
if (procid_is_me(&e->pid) && (e->op_mid == req->mid)) {
|
|
DEBUG(0, ("Trying to defer an already deferred "
|
|
"request: mid=%d, exiting\n", req->mid));
|
|
exit_server("attempt to defer a deferred request");
|
|
}
|
|
}
|
|
|
|
/* End paranoia check */
|
|
|
|
DEBUG(10,("defer_open_sharing_error: time [%u.%06u] adding deferred "
|
|
"open entry for mid %u\n",
|
|
(unsigned int)request_time.tv_sec,
|
|
(unsigned int)request_time.tv_usec,
|
|
(unsigned int)req->mid));
|
|
|
|
if (!push_deferred_smb_message(req, request_time, timeout,
|
|
(char *)state, sizeof(*state))) {
|
|
exit_server("push_deferred_smb_message failed");
|
|
}
|
|
add_deferred_open(lck, req->mid, request_time, state->id);
|
|
}
|
|
|
|
static void schedule_defer_open(struct share_mode_lock *lck,
|
|
struct timeval request_time,
|
|
struct smb_request *req)
|
|
{
|
|
struct deferred_open_record state;
|
|
|
|
/* This is a relative time, added to the absolute
|
|
request_time value to get the absolute timeout time.
|
|
Note that if this is the second or greater time we enter
|
|
this codepath for this particular request mid then
|
|
request_time is left as the absolute time of the *first*
|
|
time this request mid was processed. This is what allows
|
|
the request to eventually time out. */
|
|
|
|
struct timeval timeout;
|
|
|
|
/* Normally the smbd we asked should respond within
|
|
* OPLOCK_BREAK_TIMEOUT seconds regardless of whether
|
|
* the client did, give twice the timeout as a safety
|
|
* measure here in case the other smbd is stuck
|
|
* somewhere else. */
|
|
|
|
/*
|
|
* On OneFS, the kernel will always send an oplock_revoked message
|
|
* before this timeout is hit.
|
|
*/
|
|
timeout = timeval_set(OPLOCK_BREAK_TIMEOUT*10, 0);
|
|
|
|
/* Nothing actually uses state.delayed_for_oplocks
|
|
but it's handy to differentiate in debug messages
|
|
between a 30 second delay due to oplock break, and
|
|
a 1 second delay for share mode conflicts. */
|
|
|
|
state.delayed_for_oplocks = True;
|
|
state.failed = false;
|
|
state.id = lck->id;
|
|
|
|
if (!request_timed_out(request_time, timeout)) {
|
|
defer_open(lck, request_time, timeout, req, &state);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Open a file with a share mode. Passed in an already created files_struct.
|
|
****************************************************************************/
|
|
NTSTATUS onefs_open_file_ntcreate(connection_struct *conn,
|
|
struct smb_request *req,
|
|
const char *fname,
|
|
uint32 access_mask,
|
|
uint32 share_access,
|
|
uint32 create_disposition,
|
|
uint32 create_options,
|
|
uint32 new_dos_attributes,
|
|
int oplock_request,
|
|
struct security_descriptor *sd,
|
|
files_struct *fsp,
|
|
int *pinfo,
|
|
struct onefs_fsp_data *fsp_data,
|
|
SMB_STRUCT_STAT *psbuf)
|
|
{
|
|
int flags=0;
|
|
int flags2=0;
|
|
bool file_existed = VALID_STAT(*psbuf);
|
|
bool def_acl = False;
|
|
bool posix_open = False;
|
|
bool new_file_created = False;
|
|
bool clear_ads = False;
|
|
struct file_id id;
|
|
mode_t new_unx_mode = (mode_t)0;
|
|
mode_t unx_mode = (mode_t)0;
|
|
int info;
|
|
uint32 existing_dos_attributes = 0;
|
|
struct pending_message_list *pml = NULL;
|
|
struct timeval request_time = timeval_zero();
|
|
struct share_mode_lock *lck = NULL;
|
|
uint32 open_access_mask = access_mask;
|
|
NTSTATUS status;
|
|
int ret_flock;
|
|
char *parent_dir;
|
|
const char *newname;
|
|
int granted_oplock;
|
|
uint64_t oplock_callback_id = 0;
|
|
uint32 createfile_attributes = 0;
|
|
|
|
ZERO_STRUCT(id);
|
|
|
|
if (conn->printer) {
|
|
/*
|
|
* Printers are handled completely differently.
|
|
* Most of the passed parameters are ignored.
|
|
*/
|
|
|
|
if (pinfo) {
|
|
*pinfo = FILE_WAS_CREATED;
|
|
}
|
|
|
|
DEBUG(10, ("onefs_open_file_ntcreate: printer open fname=%s\n",
|
|
fname));
|
|
|
|
return print_fsp_open(req, conn, fname, req->vuid, fsp, psbuf);
|
|
}
|
|
|
|
if (!parent_dirname(talloc_tos(), fname, &parent_dir, &newname)) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if (new_dos_attributes & FILE_FLAG_POSIX_SEMANTICS) {
|
|
posix_open = True;
|
|
unx_mode = (mode_t)(new_dos_attributes & ~FILE_FLAG_POSIX_SEMANTICS);
|
|
new_dos_attributes = 0;
|
|
} else {
|
|
/* We add aARCH to this as this mode is only used if the file is
|
|
* created new. */
|
|
unx_mode = unix_mode(conn, new_dos_attributes | aARCH, fname,
|
|
parent_dir);
|
|
}
|
|
|
|
DEBUG(10,("onefs_open_file_ntcreate: fname=%s, dos_attrs=0x%x "
|
|
"access_mask=0x%x share_access=0x%x "
|
|
"create_disposition = 0x%x create_options=0x%x "
|
|
"unix mode=0%o oplock_request=0x%x\n",
|
|
fname, new_dos_attributes, access_mask, share_access,
|
|
create_disposition, create_options, unx_mode,
|
|
oplock_request));
|
|
|
|
/*
|
|
* Any non-stat-only open has the potential to contend oplocks, which
|
|
* means to avoid blocking in the kernel (which is unacceptable), the
|
|
* open must be deferred. In order to defer opens, req must not be
|
|
* NULL. The known cases of calling with a NULL req:
|
|
*
|
|
* 1. Open the base file of a stream: Always done stat-only
|
|
*
|
|
* 2. open_file_fchmod(), which is called from 3 places:
|
|
* A. try_chown: Posix acls only. Never called on onefs.
|
|
* B. set_ea_dos_attributes: Can't be called from onefs, because
|
|
* SMB_VFS_SETXATTR return ENOSYS.
|
|
* C. file_set_dos_mode: This would only happen if the "dos
|
|
* filemode" smb.conf parameter is set to yes. We ship with
|
|
* it off, but if a customer were to turn it on it would be
|
|
* bad.
|
|
*/
|
|
if (req == NULL && !is_stat_open(access_mask) && !is_ntfs_stream_name(fname)) {
|
|
smb_panic("NULL req on a non-stat-open!");
|
|
}
|
|
|
|
if ((req == NULL) && ((oplock_request & INTERNAL_OPEN_ONLY) == 0)) {
|
|
DEBUG(0, ("No smb request but not an internal only open!\n"));
|
|
return NT_STATUS_INTERNAL_ERROR;
|
|
}
|
|
|
|
/*
|
|
* Only non-internal opens can be deferred at all
|
|
*/
|
|
|
|
if ((req != NULL)
|
|
&& ((pml = get_open_deferred_message(req->mid)) != NULL)) {
|
|
struct deferred_open_record *state =
|
|
(struct deferred_open_record *)pml->private_data.data;
|
|
|
|
/* Remember the absolute time of the original
|
|
request with this mid. We'll use it later to
|
|
see if this has timed out. */
|
|
|
|
request_time = pml->request_time;
|
|
|
|
/* Remove the deferred open entry under lock. */
|
|
lck = get_share_mode_lock(talloc_tos(), state->id, NULL, NULL,
|
|
NULL);
|
|
if (lck == NULL) {
|
|
DEBUG(0, ("could not get share mode lock\n"));
|
|
} else {
|
|
del_deferred_open_entry(lck, req->mid);
|
|
TALLOC_FREE(lck);
|
|
}
|
|
|
|
/* Ensure we don't reprocess this message. */
|
|
remove_deferred_open_smb_message(req->mid);
|
|
|
|
/*
|
|
* When receiving a semlock_async_failure message, the
|
|
* deferred open will be marked as "failed". Returning
|
|
* INTERNAL_ERROR.
|
|
*/
|
|
if (state->failed) {
|
|
DEBUG(0, ("onefs_open_file_ntcreate: "
|
|
"semlock_async_failure detected!\n"));
|
|
return NT_STATUS_INTERNAL_ERROR;
|
|
}
|
|
}
|
|
|
|
status = check_name(conn, fname);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
if (!posix_open) {
|
|
new_dos_attributes &= SAMBA_ATTRIBUTES_MASK;
|
|
if (file_existed) {
|
|
existing_dos_attributes = dos_mode(conn, fname, psbuf);
|
|
}
|
|
}
|
|
|
|
/* Setup dos_attributes to be set by ifs_createfile */
|
|
if (lp_store_dos_attributes(SNUM(conn))) {
|
|
createfile_attributes = (new_dos_attributes | aARCH) &
|
|
~(FILE_ATTRIBUTE_NONINDEXED | FILE_ATTRIBUTE_COMPRESSED);
|
|
}
|
|
|
|
/* Ignore oplock requests if oplocks are disabled. */
|
|
if (!lp_oplocks(SNUM(conn)) || global_client_failed_oplock_break ||
|
|
IS_VETO_OPLOCK_PATH(conn, fname)) {
|
|
/* Mask off everything except the private Samba bits. */
|
|
oplock_request &= SAMBA_PRIVATE_OPLOCK_MASK;
|
|
}
|
|
|
|
/* this is for OS/2 long file names - say we don't support them */
|
|
if (!lp_posix_pathnames() && strstr(fname,".+,;=[].")) {
|
|
/* OS/2 Workplace shell fix may be main code stream in a later
|
|
* release. */
|
|
DEBUG(5,("onefs_open_file_ntcreate: OS/2 long filenames are "
|
|
"not supported.\n"));
|
|
if (use_nt_status()) {
|
|
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
return NT_STATUS_DOS(ERRDOS, ERRcannotopen);
|
|
}
|
|
|
|
switch( create_disposition ) {
|
|
/*
|
|
* Currently we're using FILE_SUPERSEDE as the same as
|
|
* FILE_OVERWRITE_IF but they really are
|
|
* different. FILE_SUPERSEDE deletes an existing file
|
|
* (requiring delete access) then recreates it.
|
|
*/
|
|
case FILE_SUPERSEDE:
|
|
/**
|
|
* @todo: Clear all file attributes?
|
|
* http://www.osronline.com/article.cfm?article=302
|
|
* create if not exist, trunc if exist
|
|
*
|
|
* If file exists replace/overwrite. If file doesn't
|
|
* exist create.
|
|
*/
|
|
flags2 |= (O_CREAT | O_TRUNC);
|
|
clear_ads = true;
|
|
break;
|
|
|
|
case FILE_OVERWRITE_IF:
|
|
/* If file exists replace/overwrite. If file doesn't
|
|
* exist create. */
|
|
flags2 |= (O_CREAT | O_TRUNC);
|
|
clear_ads = true;
|
|
break;
|
|
|
|
case FILE_OPEN:
|
|
/* If file exists open. If file doesn't exist error. */
|
|
if (!file_existed) {
|
|
DEBUG(5,("onefs_open_file_ntcreate: FILE_OPEN "
|
|
"requested for file %s and file "
|
|
"doesn't exist.\n", fname ));
|
|
errno = ENOENT;
|
|
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
break;
|
|
|
|
case FILE_OVERWRITE:
|
|
/* If file exists overwrite. If file doesn't exist
|
|
* error. */
|
|
if (!file_existed) {
|
|
DEBUG(5, ("onefs_open_file_ntcreate: "
|
|
"FILE_OVERWRITE requested for file "
|
|
"%s and file doesn't exist.\n",
|
|
fname));
|
|
errno = ENOENT;
|
|
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
flags2 |= O_TRUNC;
|
|
clear_ads = true;
|
|
break;
|
|
|
|
case FILE_CREATE:
|
|
/* If file exists error. If file doesn't exist
|
|
* create. */
|
|
if (file_existed) {
|
|
DEBUG(5, ("onefs_open_file_ntcreate: "
|
|
"FILE_CREATE requested for file %s "
|
|
"and file already exists.\n",
|
|
fname));
|
|
if (S_ISDIR(psbuf->st_ex_mode)) {
|
|
errno = EISDIR;
|
|
} else {
|
|
errno = EEXIST;
|
|
}
|
|
return map_nt_error_from_unix(errno);
|
|
}
|
|
flags2 |= (O_CREAT|O_EXCL);
|
|
break;
|
|
|
|
case FILE_OPEN_IF:
|
|
/* If file exists open. If file doesn't exist
|
|
* create. */
|
|
flags2 |= O_CREAT;
|
|
break;
|
|
|
|
default:
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Match attributes on file exists and overwrite. */
|
|
if (!posix_open && file_existed &&
|
|
((create_disposition == FILE_OVERWRITE) ||
|
|
(create_disposition == FILE_OVERWRITE_IF))) {
|
|
if (!open_match_attributes(conn, fname,
|
|
existing_dos_attributes,
|
|
new_dos_attributes, psbuf->st_ex_mode,
|
|
unx_mode, &new_unx_mode)) {
|
|
DEBUG(5, ("onefs_open_file_ntcreate: attributes "
|
|
"missmatch for file %s (%x %x) (0%o, 0%o)\n",
|
|
fname, existing_dos_attributes,
|
|
new_dos_attributes,
|
|
(unsigned int)psbuf->st_ex_mode,
|
|
(unsigned int)unx_mode ));
|
|
errno = EACCES;
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* OneFS understands MAXIMUM_ALLOWED_ACCESS, so only hack the
|
|
* access_mask, but leave the MAA for the actual open in
|
|
* open_access_mask.
|
|
*/
|
|
open_access_mask = access_mask;
|
|
if (open_access_mask & MAXIMUM_ALLOWED_ACCESS) {
|
|
access_mask |= FILE_GENERIC_ALL;
|
|
}
|
|
|
|
/* Convert GENERIC bits to specific bits. */
|
|
se_map_generic(&access_mask, &file_generic_mapping);
|
|
se_map_generic(&open_access_mask, &file_generic_mapping);
|
|
|
|
if ((flags2 & O_TRUNC) || (oplock_request & FORCE_OPLOCK_BREAK_TO_NONE)) {
|
|
/* This will cause oplock breaks. */
|
|
open_access_mask |= FILE_WRITE_DATA;
|
|
}
|
|
|
|
DEBUG(10, ("onefs_open_file_ntcreate: fname=%s, after mapping "
|
|
"open_access_mask=%#x, access_mask=0x%x\n",
|
|
fname, open_access_mask, access_mask));
|
|
|
|
/*
|
|
* Note that we ignore the append flag as append does not
|
|
* mean the same thing under DOS and Unix.
|
|
*/
|
|
|
|
if ((access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) ||
|
|
(oplock_request & FORCE_OPLOCK_BREAK_TO_NONE)) {
|
|
|
|
/*
|
|
* DENY_DOS opens are always underlying read-write on the
|
|
* file handle, no matter what the requested access mask
|
|
* says. Stock samba just sets the flags, but since
|
|
* ifs_createfile uses the access_mask, it must be updated as
|
|
* well. This allows BASE-DENY* to pass.
|
|
*/
|
|
if (create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS) {
|
|
|
|
DEBUG(10,("onefs_open_file_ntcreate: deny_dos: "
|
|
"Adding O_RDWR to flags "
|
|
"(0x%x) and some READ bits to "
|
|
"open_access_mask (0x%x)\n",
|
|
flags, open_access_mask));
|
|
|
|
flags = O_RDWR;
|
|
open_access_mask |= (FILE_READ_ATTRIBUTES |
|
|
FILE_READ_DATA | FILE_READ_EA | FILE_EXECUTE);
|
|
|
|
} else if (access_mask & (FILE_READ_ATTRIBUTES |
|
|
FILE_READ_DATA |
|
|
FILE_READ_EA |
|
|
FILE_EXECUTE)) {
|
|
flags = O_RDWR;
|
|
} else {
|
|
flags = O_WRONLY;
|
|
}
|
|
} else {
|
|
flags = O_RDONLY;
|
|
}
|
|
|
|
/* Currently we only look at FILE_WRITE_THROUGH for create options. */
|
|
#if defined(O_SYNC)
|
|
if ((create_options & FILE_WRITE_THROUGH) &&
|
|
lp_strict_sync(SNUM(conn))) {
|
|
flags2 |= O_SYNC;
|
|
}
|
|
#endif /* O_SYNC */
|
|
|
|
if (posix_open && (access_mask & FILE_APPEND_DATA)) {
|
|
flags2 |= O_APPEND;
|
|
}
|
|
|
|
if (!posix_open && !CAN_WRITE(conn)) {
|
|
/*
|
|
* We should really return a permission denied error if either
|
|
* O_CREAT or O_TRUNC are set, but for compatibility with
|
|
* older versions of Samba we just AND them out.
|
|
*/
|
|
flags2 &= ~(O_CREAT|O_TRUNC);
|
|
|
|
/* Deny DELETE_ACCESS explicitly if the share is read only. */
|
|
if (access_mask & DELETE_ACCESS) {
|
|
return map_nt_error_from_unix(EACCES);
|
|
}
|
|
}
|
|
|
|
/* Ensure we can't write on a read-only share or file. */
|
|
if (flags != O_RDONLY && file_existed &&
|
|
(!CAN_WRITE(conn) || IS_DOS_READONLY(existing_dos_attributes))) {
|
|
DEBUG(5, ("onefs_open_file_ntcreate: write access requested "
|
|
"for file %s on read only %s\n",
|
|
fname, !CAN_WRITE(conn) ? "share" : "file" ));
|
|
errno = EACCES;
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
DEBUG(10, ("fsp = %p\n", fsp));
|
|
|
|
fsp->file_id = vfs_file_id_from_sbuf(conn, psbuf);
|
|
fsp->share_access = share_access;
|
|
fsp->fh->private_options = create_options;
|
|
fsp->access_mask = open_access_mask; /* We change this to the
|
|
* requested access_mask after
|
|
* the open is done. */
|
|
fsp->posix_open = posix_open;
|
|
|
|
/* Ensure no SAMBA_PRIVATE bits can be set. */
|
|
fsp->oplock_type = (oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);
|
|
|
|
if (timeval_is_zero(&request_time)) {
|
|
request_time = fsp->open_time;
|
|
}
|
|
|
|
if (file_existed) {
|
|
struct timespec old_write_time = psbuf->st_ex_mtime;
|
|
id = vfs_file_id_from_sbuf(conn, psbuf);
|
|
|
|
lck = get_share_mode_lock(talloc_tos(), id,
|
|
conn->connectpath,
|
|
fname, &old_write_time);
|
|
|
|
if (lck == NULL) {
|
|
DEBUG(0, ("Could not get share mode lock\n"));
|
|
return NT_STATUS_SHARING_VIOLATION;
|
|
}
|
|
|
|
if (lck->delete_on_close) {
|
|
/* DELETE_PENDING is not deferred for a second */
|
|
TALLOC_FREE(lck);
|
|
return NT_STATUS_DELETE_PENDING;
|
|
}
|
|
}
|
|
|
|
SMB_ASSERT(!file_existed || (lck != NULL));
|
|
|
|
/*
|
|
* Ensure we pay attention to default ACLs on directories. May be
|
|
* neccessary depending on ACL policies.
|
|
*/
|
|
if ((flags2 & O_CREAT) && lp_inherit_acls(SNUM(conn)) &&
|
|
(def_acl = directory_has_default_acl(conn, parent_dir))) {
|
|
unx_mode = 0777;
|
|
}
|
|
|
|
DEBUG(4,("calling onefs_open_file with flags=0x%X flags2=0x%X "
|
|
"mode=0%o, access_mask = 0x%x, open_access_mask = 0x%x\n",
|
|
(unsigned int)flags, (unsigned int)flags2,
|
|
(unsigned int)unx_mode, (unsigned int)access_mask,
|
|
(unsigned int)open_access_mask));
|
|
|
|
/*
|
|
* Since the open is guaranteed to be stat only if req == NULL, a
|
|
* callback record is only needed if req != NULL.
|
|
*/
|
|
if (req) {
|
|
SMB_ASSERT(fsp_data);
|
|
oplock_callback_id = onefs_oplock_wait_record(req->mid);
|
|
if (oplock_callback_id == 0) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
} else {
|
|
/*
|
|
* It is also already asserted it's either a stream or a
|
|
* stat-only open at this point.
|
|
*/
|
|
SMB_ASSERT(fsp->oplock_type == NO_OPLOCK);
|
|
}
|
|
|
|
/* Do the open. */
|
|
status = onefs_open_file(fsp,
|
|
conn,
|
|
req,
|
|
parent_dir,
|
|
newname,
|
|
fname,
|
|
psbuf,
|
|
flags|flags2,
|
|
unx_mode,
|
|
access_mask,
|
|
open_access_mask,
|
|
fsp->oplock_type,
|
|
oplock_callback_id,
|
|
share_access,
|
|
create_options,
|
|
createfile_attributes,
|
|
sd,
|
|
&granted_oplock);
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
|
|
/* OneFS Oplock Handling */
|
|
if (errno == EINPROGRESS) {
|
|
|
|
if (lck == NULL) {
|
|
|
|
struct deferred_open_record state;
|
|
struct timespec old_write_time;
|
|
|
|
old_write_time = psbuf->st_ex_mtime;
|
|
|
|
DEBUG(3, ("Someone created file %s with an "
|
|
"oplock after we looked: Retrying\n",
|
|
fname));
|
|
/*
|
|
* We hit the race that when we did the stat
|
|
* on the file it did not exist, and someone
|
|
* has created it in between the stat and the
|
|
* open_file() call. Just retry immediately.
|
|
*/
|
|
id = vfs_file_id_from_sbuf(conn, psbuf);
|
|
if (!(lck = get_share_mode_lock(talloc_tos(),
|
|
id, conn->connectpath, fname,
|
|
&old_write_time))) {
|
|
/*
|
|
* Emergency exit
|
|
*/
|
|
DEBUG(0, ("onefs_open_file_ntcreate: "
|
|
"Could not get share mode "
|
|
"lock for %s\n", fname));
|
|
status = NT_STATUS_SHARING_VIOLATION;
|
|
goto cleanup_destroy;
|
|
}
|
|
|
|
state.delayed_for_oplocks = False;
|
|
state.id = id;
|
|
|
|
if (req != NULL) {
|
|
defer_open(lck, request_time,
|
|
timeval_zero(), req, &state);
|
|
}
|
|
goto cleanup_destroy;
|
|
}
|
|
/* Waiting for an oplock */
|
|
DEBUG(5,("Async createfile because a client has an "
|
|
"oplock on %s\n", fname));
|
|
|
|
SMB_ASSERT(req);
|
|
schedule_defer_open(lck, request_time, req);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Check for a sharing violation */
|
|
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
|
|
uint32 can_access_mask;
|
|
bool can_access = True;
|
|
|
|
/* Check if this can be done with the deny_dos and fcb
|
|
* calls. */
|
|
|
|
/* Try to find dup fsp if possible. */
|
|
if (create_options &
|
|
(NTCREATEX_OPTIONS_PRIVATE_DENY_DOS|
|
|
NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
|
|
|
|
if (req == NULL) {
|
|
DEBUG(0, ("DOS open without an SMB "
|
|
"request!\n"));
|
|
status = NT_STATUS_INTERNAL_ERROR;
|
|
goto cleanup_destroy;
|
|
}
|
|
|
|
/* Use the client requested access mask here,
|
|
* not the one we open with. */
|
|
status = fcb_or_dos_open(req,
|
|
conn,
|
|
fsp,
|
|
fname,
|
|
id,
|
|
req->smbpid,
|
|
req->vuid,
|
|
access_mask,
|
|
share_access,
|
|
create_options);
|
|
|
|
if (NT_STATUS_IS_OK(status)) {
|
|
if (pinfo) {
|
|
*pinfo = FILE_WAS_OPENED;
|
|
}
|
|
status = NT_STATUS_OK;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This next line is a subtlety we need for
|
|
* MS-Access. If a file open will fail due to share
|
|
* permissions and also for security (access) reasons,
|
|
* we need to return the access failed error, not the
|
|
* share error. We can't open the file due to kernel
|
|
* oplock deadlock (it's possible we failed above on
|
|
* the open_mode_check()) so use a userspace check.
|
|
*/
|
|
|
|
if (flags & O_RDWR) {
|
|
can_access_mask = FILE_READ_DATA|FILE_WRITE_DATA;
|
|
} else if (flags & O_WRONLY) {
|
|
can_access_mask = FILE_WRITE_DATA;
|
|
} else {
|
|
can_access_mask = FILE_READ_DATA;
|
|
}
|
|
|
|
if (((can_access_mask & FILE_WRITE_DATA) && !CAN_WRITE(conn)) ||
|
|
!can_access_file_data(conn,fname,psbuf,can_access_mask)) {
|
|
can_access = False;
|
|
}
|
|
|
|
/*
|
|
* If we're returning a share violation, ensure we
|
|
* cope with the braindead 1 second delay.
|
|
*/
|
|
if (!(oplock_request & INTERNAL_OPEN_ONLY) &&
|
|
lp_defer_sharing_violations()) {
|
|
struct timeval timeout;
|
|
struct deferred_open_record state;
|
|
int timeout_usecs;
|
|
|
|
/* this is a hack to speed up torture tests
|
|
in 'make test' */
|
|
timeout_usecs = lp_parm_int(SNUM(conn),
|
|
"smbd","sharedelay",
|
|
SHARING_VIOLATION_USEC_WAIT);
|
|
|
|
/* This is a relative time, added to the
|
|
absolute request_time value to get the
|
|
absolute timeout time. Note that if this
|
|
is the second or greater time we enter this
|
|
codepath for this particular request mid
|
|
then request_time is left as the absolute
|
|
time of the *first* time this request mid
|
|
was processed. This is what allows the
|
|
request to eventually time out. */
|
|
|
|
timeout = timeval_set(0, timeout_usecs);
|
|
|
|
/* Nothing actually uses
|
|
state.delayed_for_oplocks but it's handy to
|
|
differentiate in debug messages between a
|
|
30 second delay due to oplock break, and a
|
|
1 second delay for share mode conflicts. */
|
|
|
|
state.delayed_for_oplocks = False;
|
|
state.id = id;
|
|
state.failed = false;
|
|
|
|
if ((req != NULL)
|
|
&& !request_timed_out(request_time,
|
|
timeout)) {
|
|
defer_open(lck, request_time, timeout,
|
|
req, &state);
|
|
}
|
|
}
|
|
|
|
if (can_access) {
|
|
/*
|
|
* We have detected a sharing violation here
|
|
* so return the correct error code
|
|
*/
|
|
status = NT_STATUS_SHARING_VIOLATION;
|
|
} else {
|
|
status = NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
goto cleanup_destroy;
|
|
}
|
|
|
|
/*
|
|
* Normal error, for example EACCES
|
|
*/
|
|
cleanup_destroy:
|
|
if (oplock_callback_id != 0) {
|
|
destroy_onefs_callback_record(oplock_callback_id);
|
|
}
|
|
cleanup:
|
|
TALLOC_FREE(lck);
|
|
return status;
|
|
}
|
|
|
|
fsp->oplock_type = granted_oplock;
|
|
|
|
if (oplock_callback_id != 0) {
|
|
onefs_set_oplock_callback(oplock_callback_id, fsp);
|
|
fsp_data->oplock_callback_id = oplock_callback_id;
|
|
} else {
|
|
SMB_ASSERT(fsp->oplock_type == NO_OPLOCK);
|
|
}
|
|
|
|
if (!file_existed) {
|
|
struct timespec old_write_time = psbuf->st_ex_mtime;
|
|
/*
|
|
* Deal with the race condition where two smbd's detect the
|
|
* file doesn't exist and do the create at the same time. One
|
|
* of them will win and set a share mode, the other (ie. this
|
|
* one) should check if the requested share mode for this
|
|
* create is allowed.
|
|
*/
|
|
|
|
/*
|
|
* Now the file exists and fsp is successfully opened,
|
|
* fsp->dev and fsp->inode are valid and should replace the
|
|
* dev=0,inode=0 from a non existent file. Spotted by
|
|
* Nadav Danieli <nadavd@exanet.com>. JRA.
|
|
*/
|
|
|
|
id = fsp->file_id;
|
|
|
|
lck = get_share_mode_lock(talloc_tos(), id,
|
|
conn->connectpath,
|
|
fname, &old_write_time);
|
|
|
|
if (lck == NULL) {
|
|
DEBUG(0, ("onefs_open_file_ntcreate: Could not get "
|
|
"share mode lock for %s\n", fname));
|
|
fd_close(fsp);
|
|
return NT_STATUS_SHARING_VIOLATION;
|
|
}
|
|
|
|
if (lck->delete_on_close) {
|
|
status = NT_STATUS_DELETE_PENDING;
|
|
}
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
struct deferred_open_record state;
|
|
|
|
fd_close(fsp);
|
|
|
|
state.delayed_for_oplocks = False;
|
|
state.id = id;
|
|
|
|
/* Do it all over again immediately. In the second
|
|
* round we will find that the file existed and handle
|
|
* the DELETE_PENDING and FCB cases correctly. No need
|
|
* to duplicate the code here. Essentially this is a
|
|
* "goto top of this function", but don't tell
|
|
* anybody... */
|
|
|
|
if (req != NULL) {
|
|
defer_open(lck, request_time, timeval_zero(),
|
|
req, &state);
|
|
}
|
|
TALLOC_FREE(lck);
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* We exit this block with the share entry *locked*.....
|
|
*/
|
|
|
|
}
|
|
|
|
SMB_ASSERT(lck != NULL);
|
|
|
|
/* Delete streams if create_disposition requires it */
|
|
if (file_existed && clear_ads && !is_ntfs_stream_name(fname)) {
|
|
status = delete_all_streams(conn, fname);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
TALLOC_FREE(lck);
|
|
fd_close(fsp);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
/* note that we ignore failure for the following. It is
|
|
basically a hack for NFS, and NFS will never set one of
|
|
these only read them. Nobody but Samba can ever set a deny
|
|
mode and we have already checked our more authoritative
|
|
locking database for permission to set this deny mode. If
|
|
the kernel refuses the operations then the kernel is wrong.
|
|
note that GPFS supports it as well - jmcd */
|
|
|
|
if (fsp->fh->fd != -1) {
|
|
ret_flock = SMB_VFS_KERNEL_FLOCK(fsp, share_access);
|
|
if(ret_flock == -1 ){
|
|
|
|
TALLOC_FREE(lck);
|
|
fd_close(fsp);
|
|
return NT_STATUS_SHARING_VIOLATION;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* At this point onwards, we can guarentee that the share entry
|
|
* is locked, whether we created the file or not, and that the
|
|
* deny mode is compatible with all current opens.
|
|
*/
|
|
|
|
/* Record the options we were opened with. */
|
|
fsp->share_access = share_access;
|
|
fsp->fh->private_options = create_options;
|
|
/*
|
|
* According to Samba4, SEC_FILE_READ_ATTRIBUTE is always granted,
|
|
*/
|
|
fsp->access_mask = access_mask | FILE_READ_ATTRIBUTES;
|
|
|
|
if (file_existed) {
|
|
/* stat opens on existing files don't get oplocks. */
|
|
if (is_stat_open(open_access_mask)) {
|
|
fsp->oplock_type = NO_OPLOCK;
|
|
}
|
|
|
|
if (!(flags2 & O_TRUNC)) {
|
|
info = FILE_WAS_OPENED;
|
|
} else {
|
|
info = FILE_WAS_OVERWRITTEN;
|
|
}
|
|
} else {
|
|
info = FILE_WAS_CREATED;
|
|
}
|
|
|
|
if (pinfo) {
|
|
*pinfo = info;
|
|
}
|
|
|
|
/*
|
|
* Setup the oplock info in both the shared memory and
|
|
* file structs.
|
|
*/
|
|
|
|
if ((fsp->oplock_type != NO_OPLOCK) &&
|
|
(fsp->oplock_type != FAKE_LEVEL_II_OPLOCK)) {
|
|
if (!set_file_oplock(fsp, fsp->oplock_type)) {
|
|
/* Could not get the kernel oplock */
|
|
fsp->oplock_type = NO_OPLOCK;
|
|
}
|
|
}
|
|
|
|
if (fsp->oplock_type == LEVEL_II_OPLOCK &&
|
|
(!lp_level2_oplocks(SNUM(conn)) ||
|
|
!(global_client_caps & CAP_LEVEL_II_OPLOCKS))) {
|
|
|
|
DEBUG(5, ("Downgrading level2 oplock on open "
|
|
"because level2 oplocks = off\n"));
|
|
|
|
release_file_oplock(fsp);
|
|
}
|
|
|
|
if (info == FILE_WAS_OVERWRITTEN || info == FILE_WAS_CREATED ||
|
|
info == FILE_WAS_SUPERSEDED) {
|
|
new_file_created = True;
|
|
}
|
|
|
|
set_share_mode(lck, fsp, conn->server_info->utok.uid, 0,
|
|
fsp->oplock_type);
|
|
|
|
/* Handle strange delete on close create semantics. */
|
|
if (create_options & FILE_DELETE_ON_CLOSE) {
|
|
status = can_set_delete_on_close(fsp, True, new_dos_attributes);
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
/* Remember to delete the mode we just added. */
|
|
del_share_mode(lck, fsp);
|
|
TALLOC_FREE(lck);
|
|
fd_close(fsp);
|
|
return status;
|
|
}
|
|
/* Note that here we set the *inital* delete on close flag,
|
|
not the regular one. The magic gets handled in close. */
|
|
fsp->initial_delete_on_close = True;
|
|
}
|
|
|
|
/*
|
|
* Take care of inherited ACLs on created files - if default ACL not
|
|
* selected.
|
|
* May be necessary depending on acl policies.
|
|
*/
|
|
if (!posix_open && !file_existed && !def_acl && !(VALID_STAT(*psbuf)
|
|
&& (psbuf->st_ex_flags & SF_HASNTFSACL))) {
|
|
|
|
int saved_errno = errno; /* We might get ENOSYS in the next
|
|
* call.. */
|
|
|
|
if (SMB_VFS_FCHMOD_ACL(fsp, unx_mode) == -1 &&
|
|
errno == ENOSYS) {
|
|
errno = saved_errno; /* Ignore ENOSYS */
|
|
}
|
|
|
|
} else if (new_unx_mode) {
|
|
|
|
int ret = -1;
|
|
|
|
/* Attributes need changing. File already existed. */
|
|
|
|
{
|
|
int saved_errno = errno; /* We might get ENOSYS in the
|
|
* next call.. */
|
|
ret = SMB_VFS_FCHMOD_ACL(fsp, new_unx_mode);
|
|
|
|
if (ret == -1 && errno == ENOSYS) {
|
|
errno = saved_errno; /* Ignore ENOSYS */
|
|
} else {
|
|
DEBUG(5, ("onefs_open_file_ntcreate: reset "
|
|
"attributes of file %s to 0%o\n",
|
|
fname, (unsigned int)new_unx_mode));
|
|
ret = 0; /* Don't do the fchmod below. */
|
|
}
|
|
}
|
|
|
|
if ((ret == -1) &&
|
|
(SMB_VFS_FCHMOD(fsp, new_unx_mode) == -1))
|
|
DEBUG(5, ("onefs_open_file_ntcreate: failed to reset "
|
|
"attributes of file %s to 0%o\n",
|
|
fname, (unsigned int)new_unx_mode));
|
|
}
|
|
|
|
/* If this is a successful open, we must remove any deferred open
|
|
* records. */
|
|
if (req != NULL) {
|
|
del_deferred_open_entry(lck, req->mid);
|
|
}
|
|
TALLOC_FREE(lck);
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
Open a directory from an NT SMB call.
|
|
****************************************************************************/
|
|
static NTSTATUS onefs_open_directory(connection_struct *conn,
|
|
struct smb_request *req,
|
|
const char *fname,
|
|
uint32 access_mask,
|
|
uint32 share_access,
|
|
uint32 create_disposition,
|
|
uint32 create_options,
|
|
uint32 file_attributes,
|
|
struct security_descriptor *sd,
|
|
files_struct **result,
|
|
int *pinfo,
|
|
SMB_STRUCT_STAT *psbuf)
|
|
{
|
|
files_struct *fsp = NULL;
|
|
struct share_mode_lock *lck = NULL;
|
|
NTSTATUS status;
|
|
struct timespec mtimespec;
|
|
int info = 0;
|
|
char *parent_dir;
|
|
const char *dirname;
|
|
bool posix_open = false;
|
|
uint32 create_flags = 0;
|
|
uint32 mode = lp_dir_mask(SNUM(conn));
|
|
|
|
DEBUG(5, ("onefs_open_directory: opening directory %s, "
|
|
"access_mask = 0x%x, "
|
|
"share_access = 0x%x create_options = 0x%x, "
|
|
"create_disposition = 0x%x, file_attributes = 0x%x\n",
|
|
fname, (unsigned int)access_mask, (unsigned int)share_access,
|
|
(unsigned int)create_options, (unsigned int)create_disposition,
|
|
(unsigned int)file_attributes));
|
|
|
|
if (!(file_attributes & FILE_FLAG_POSIX_SEMANTICS) &&
|
|
(conn->fs_capabilities & FILE_NAMED_STREAMS) &&
|
|
is_ntfs_stream_name(fname)) {
|
|
DEBUG(2, ("onefs_open_directory: %s is a stream name!\n", fname));
|
|
return NT_STATUS_NOT_A_DIRECTORY;
|
|
}
|
|
|
|
switch (create_disposition) {
|
|
case FILE_OPEN:
|
|
/* If directory exists open. If directory doesn't
|
|
* exist error. */
|
|
create_flags = 0;
|
|
info = FILE_WAS_OPENED;
|
|
break;
|
|
case FILE_CREATE:
|
|
/* If directory exists error. If directory doesn't
|
|
* exist create. */
|
|
create_flags = O_CREAT | O_EXCL;
|
|
info = FILE_WAS_CREATED;
|
|
break;
|
|
case FILE_OPEN_IF:
|
|
/* If directory exists open. If directory doesn't
|
|
* exist create. */
|
|
|
|
/* Note: in order to return whether the directory was
|
|
* opened or created, we first try to open and then try
|
|
* to create. */
|
|
create_flags = 0;
|
|
info = FILE_WAS_OPENED;
|
|
break;
|
|
case FILE_SUPERSEDE:
|
|
case FILE_OVERWRITE:
|
|
case FILE_OVERWRITE_IF:
|
|
default:
|
|
DEBUG(5, ("onefs_open_directory: invalid "
|
|
"create_disposition 0x%x for directory %s\n",
|
|
(unsigned int)create_disposition, fname));
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/*
|
|
* Check for write access to the share. Done in mkdir_internal() in
|
|
* mainline samba.
|
|
*/
|
|
if (!CAN_WRITE(conn) && (create_flags & O_CREAT)) {
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
/* Get parent dirname */
|
|
if (!parent_dirname(talloc_tos(), fname, &parent_dir, &dirname)) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
if (file_attributes & FILE_FLAG_POSIX_SEMANTICS) {
|
|
posix_open = true;
|
|
mode = (mode_t)(file_attributes & ~FILE_FLAG_POSIX_SEMANTICS);
|
|
file_attributes = 0;
|
|
} else {
|
|
mode = unix_mode(conn, aDIR, fname, parent_dir);
|
|
}
|
|
|
|
/*
|
|
* The NONINDEXED and COMPRESSED bits seem to always be cleared on
|
|
* directories, no matter if you specify that they should be set.
|
|
*/
|
|
file_attributes &=
|
|
~(FILE_ATTRIBUTE_NONINDEXED | FILE_ATTRIBUTE_COMPRESSED);
|
|
|
|
status = file_new(req, conn, &fsp);
|
|
if(!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Actual open with retry magic to handle FILE_OPEN_IF which is
|
|
* unique because the kernel won't tell us if the file was opened or
|
|
* created.
|
|
*/
|
|
retry_open:
|
|
fsp->fh->fd = onefs_sys_create_file(conn,
|
|
-1,
|
|
fname,
|
|
access_mask,
|
|
access_mask,
|
|
share_access,
|
|
create_options,
|
|
create_flags | O_DIRECTORY,
|
|
mode,
|
|
0,
|
|
0,
|
|
sd,
|
|
file_attributes,
|
|
NULL);
|
|
|
|
if (fsp->fh->fd == -1) {
|
|
DEBUG(3, ("Error opening %s. Errno=%d (%s).\n", fname, errno,
|
|
strerror(errno)));
|
|
SMB_ASSERT(errno != EINPROGRESS);
|
|
|
|
if (create_disposition == FILE_OPEN_IF) {
|
|
if (errno == ENOENT) {
|
|
/* Try again, creating it this time. */
|
|
create_flags = O_CREAT | O_EXCL;
|
|
info = FILE_WAS_CREATED;
|
|
goto retry_open;
|
|
} else if (errno == EEXIST) {
|
|
/* Uggh. Try again again. */
|
|
create_flags = 0;
|
|
info = FILE_WAS_OPENED;
|
|
goto retry_open;
|
|
}
|
|
}
|
|
|
|
/* Error cases below: */
|
|
file_free(req, fsp);
|
|
|
|
if ((errno == ENOENT) && (create_disposition == FILE_OPEN)) {
|
|
DEBUG(5,("onefs_open_directory: FILE_OPEN requested "
|
|
"for directory %s and it doesn't "
|
|
"exist.\n", fname ));
|
|
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
} else if ((errno == EEXIST) &&
|
|
(create_disposition == FILE_CREATE)) {
|
|
DEBUG(5,("onefs_open_directory: FILE_CREATE "
|
|
"requested for directory %s and it "
|
|
"already exists.\n", fname ));
|
|
return NT_STATUS_OBJECT_NAME_COLLISION;
|
|
} else if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
|
|
/* Catch sharing violations. */
|
|
return NT_STATUS_SHARING_VIOLATION;
|
|
}
|
|
|
|
return map_nt_error_from_unix(errno);
|
|
}
|
|
|
|
if (info == FILE_WAS_CREATED) {
|
|
|
|
/* Pulled from mkdir_internal() */
|
|
if (SMB_VFS_LSTAT(conn, fname, psbuf) == -1) {
|
|
DEBUG(2, ("Could not stat directory '%s' just "
|
|
"created: %s\n",fname, strerror(errno)));
|
|
return map_nt_error_from_unix(errno);
|
|
}
|
|
|
|
if (!S_ISDIR(psbuf->st_ex_mode)) {
|
|
DEBUG(0, ("Directory just '%s' created is not a "
|
|
"directory\n", fname));
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
if (!posix_open) {
|
|
/*
|
|
* Check if high bits should have been set, then (if
|
|
* bits are missing): add them. Consider bits
|
|
* automagically set by UNIX, i.e. SGID bit from
|
|
* parent dir.
|
|
*/
|
|
if (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
|
|
(mode & ~psbuf->st_ex_mode)) {
|
|
SMB_VFS_CHMOD(conn, fname, (psbuf->st_ex_mode |
|
|
(mode & ~psbuf->st_ex_mode)));
|
|
}
|
|
}
|
|
|
|
/* Change the owner if required. */
|
|
if (lp_inherit_owner(SNUM(conn))) {
|
|
change_dir_owner_to_parent(conn, parent_dir, fname,
|
|
psbuf);
|
|
}
|
|
|
|
notify_fname(conn, NOTIFY_ACTION_ADDED,
|
|
FILE_NOTIFY_CHANGE_DIR_NAME, fname);
|
|
}
|
|
|
|
/* Stat the fd for Samba bookkeeping. */
|
|
if(SMB_VFS_FSTAT(fsp, psbuf) != 0) {
|
|
fd_close(fsp);
|
|
file_free(req, fsp);
|
|
return map_nt_error_from_unix(errno);
|
|
}
|
|
|
|
/* Setup the files_struct for it. */
|
|
fsp->mode = psbuf->st_ex_mode;
|
|
fsp->file_id = vfs_file_id_from_sbuf(conn, psbuf);
|
|
fsp->vuid = req ? req->vuid : UID_FIELD_INVALID;
|
|
fsp->file_pid = req ? req->smbpid : 0;
|
|
fsp->can_lock = False;
|
|
fsp->can_read = False;
|
|
fsp->can_write = False;
|
|
|
|
fsp->share_access = share_access;
|
|
fsp->fh->private_options = create_options;
|
|
/*
|
|
* According to Samba4, SEC_FILE_READ_ATTRIBUTE is always granted,
|
|
*/
|
|
fsp->access_mask = access_mask | FILE_READ_ATTRIBUTES;
|
|
fsp->print_file = False;
|
|
fsp->modified = False;
|
|
fsp->oplock_type = NO_OPLOCK;
|
|
fsp->sent_oplock_break = NO_BREAK_SENT;
|
|
fsp->is_directory = True;
|
|
fsp->posix_open = posix_open;
|
|
|
|
string_set(&fsp->fsp_name,fname);
|
|
|
|
mtimespec = psbuf->st_ex_mtime;
|
|
|
|
/*
|
|
* Still set the samba share mode lock for correct delete-on-close
|
|
* semantics and to make smbstatus more useful.
|
|
*/
|
|
lck = get_share_mode_lock(talloc_tos(), fsp->file_id,
|
|
conn->connectpath,
|
|
fname, &mtimespec);
|
|
|
|
if (lck == NULL) {
|
|
DEBUG(0, ("onefs_open_directory: Could not get share mode "
|
|
"lock for %s\n", fname));
|
|
fd_close(fsp);
|
|
file_free(req, fsp);
|
|
return NT_STATUS_SHARING_VIOLATION;
|
|
}
|
|
|
|
if (lck->delete_on_close) {
|
|
TALLOC_FREE(lck);
|
|
fd_close(fsp);
|
|
file_free(req, fsp);
|
|
return NT_STATUS_DELETE_PENDING;
|
|
}
|
|
|
|
set_share_mode(lck, fsp, conn->server_info->utok.uid, 0, NO_OPLOCK);
|
|
|
|
/*
|
|
* For directories the delete on close bit at open time seems
|
|
* always to be honored on close... See test 19 in Samba4 BASE-DELETE.
|
|
*/
|
|
if (create_options & FILE_DELETE_ON_CLOSE) {
|
|
status = can_set_delete_on_close(fsp, True, 0);
|
|
if (!NT_STATUS_IS_OK(status) &&
|
|
!NT_STATUS_EQUAL(status, NT_STATUS_DIRECTORY_NOT_EMPTY)) {
|
|
TALLOC_FREE(lck);
|
|
fd_close(fsp);
|
|
file_free(req, fsp);
|
|
return status;
|
|
}
|
|
|
|
if (NT_STATUS_IS_OK(status)) {
|
|
/* Note that here we set the *inital* delete on close flag,
|
|
not the regular one. The magic gets handled in close. */
|
|
fsp->initial_delete_on_close = True;
|
|
}
|
|
}
|
|
|
|
TALLOC_FREE(lck);
|
|
|
|
if (pinfo) {
|
|
*pinfo = info;
|
|
}
|
|
|
|
*result = fsp;
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/*
|
|
* Wrapper around onefs_open_file_ntcreate and onefs_open_directory.
|
|
*/
|
|
static NTSTATUS onefs_create_file_unixpath(connection_struct *conn,
|
|
struct smb_request *req,
|
|
const char *fname,
|
|
uint32_t access_mask,
|
|
uint32_t share_access,
|
|
uint32_t create_disposition,
|
|
uint32_t create_options,
|
|
uint32_t file_attributes,
|
|
uint32_t oplock_request,
|
|
uint64_t allocation_size,
|
|
struct security_descriptor *sd,
|
|
struct ea_list *ea_list,
|
|
files_struct **result,
|
|
int *pinfo,
|
|
struct onefs_fsp_data *fsp_data,
|
|
SMB_STRUCT_STAT *psbuf)
|
|
{
|
|
SMB_STRUCT_STAT sbuf;
|
|
int info = FILE_WAS_OPENED;
|
|
files_struct *base_fsp = NULL;
|
|
files_struct *fsp = NULL;
|
|
NTSTATUS status;
|
|
|
|
DEBUG(10,("onefs_create_file_unixpath: access_mask = 0x%x "
|
|
"file_attributes = 0x%x, share_access = 0x%x, "
|
|
"create_disposition = 0x%x create_options = 0x%x "
|
|
"oplock_request = 0x%x ea_list = 0x%p, sd = 0x%p, "
|
|
"fname = %s\n",
|
|
(unsigned int)access_mask,
|
|
(unsigned int)file_attributes,
|
|
(unsigned int)share_access,
|
|
(unsigned int)create_disposition,
|
|
(unsigned int)create_options,
|
|
(unsigned int)oplock_request,
|
|
ea_list, sd, fname));
|
|
|
|
if (create_options & FILE_OPEN_BY_FILE_ID) {
|
|
status = NT_STATUS_NOT_SUPPORTED;
|
|
goto fail;
|
|
}
|
|
|
|
if (create_options & NTCREATEX_OPTIONS_INVALID_PARAM_MASK) {
|
|
status = NT_STATUS_INVALID_PARAMETER;
|
|
goto fail;
|
|
}
|
|
|
|
if (req == NULL) {
|
|
SMB_ASSERT((oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK) ==
|
|
NO_OPLOCK);
|
|
oplock_request |= INTERNAL_OPEN_ONLY;
|
|
}
|
|
|
|
if (psbuf != NULL) {
|
|
sbuf = *psbuf;
|
|
}
|
|
else {
|
|
if (SMB_VFS_STAT(conn, fname, &sbuf) == -1) {
|
|
SET_STAT_INVALID(sbuf);
|
|
}
|
|
}
|
|
|
|
if (lp_parm_bool(SNUM(conn), PARM_ONEFS_TYPE,
|
|
PARM_IGNORE_SACLS, PARM_IGNORE_SACLS_DEFAULT)) {
|
|
access_mask &= ~SYSTEM_SECURITY_ACCESS;
|
|
}
|
|
|
|
if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
|
|
&& (access_mask & DELETE_ACCESS)
|
|
&& !is_ntfs_stream_name(fname)) {
|
|
/*
|
|
* We can't open a file with DELETE access if any of the
|
|
* streams is open without FILE_SHARE_DELETE
|
|
*/
|
|
status = open_streams_for_delete(conn, fname);
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
|
|
&& is_ntfs_stream_name(fname)) {
|
|
char *base;
|
|
uint32 base_create_disposition;
|
|
|
|
if (create_options & FILE_DIRECTORY_FILE) {
|
|
status = NT_STATUS_NOT_A_DIRECTORY;
|
|
goto fail;
|
|
}
|
|
|
|
status = onefs_split_ntfs_stream_name(talloc_tos(), fname,
|
|
&base, NULL);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DEBUG(10, ("onefs_create_file_unixpath: "
|
|
"split_ntfs_stream_name failed: %s\n",
|
|
nt_errstr(status)));
|
|
goto fail;
|
|
}
|
|
|
|
SMB_ASSERT(!is_ntfs_stream_name(base)); /* paranoia.. */
|
|
|
|
switch (create_disposition) {
|
|
case FILE_OPEN:
|
|
base_create_disposition = FILE_OPEN;
|
|
break;
|
|
default:
|
|
base_create_disposition = FILE_OPEN_IF;
|
|
break;
|
|
}
|
|
|
|
status = onefs_create_file_unixpath(
|
|
conn, /* conn */
|
|
NULL, /* req */
|
|
base, /* fname */
|
|
SYNCHRONIZE_ACCESS, /* access_mask */
|
|
(FILE_SHARE_READ |
|
|
FILE_SHARE_WRITE |
|
|
FILE_SHARE_DELETE), /* share_access */
|
|
base_create_disposition, /* create_disposition*/
|
|
0, /* create_options */
|
|
file_attributes, /* file_attributes */
|
|
NO_OPLOCK, /* oplock_request */
|
|
0, /* allocation_size */
|
|
NULL, /* sd */
|
|
NULL, /* ea_list */
|
|
&base_fsp, /* result */
|
|
NULL, /* pinfo */
|
|
NULL, /* fsp_data */
|
|
NULL); /* psbuf */
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DEBUG(10, ("onefs_create_file_unixpath for base %s "
|
|
"failed: %s\n", base, nt_errstr(status)));
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* Testing against windows xp/2003/vista shows that oplocks
|
|
* can actually be requested and granted on streams (see the
|
|
* RAW-OPLOCK-STREAM1 smbtorture test).
|
|
*/
|
|
if ((oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK) !=
|
|
NO_OPLOCK) {
|
|
DEBUG(5, ("Oplock(%d) being requested on a stream! "
|
|
"Ignoring oplock request: fname=%s\n",
|
|
oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK,
|
|
fname));
|
|
/* Request NO_OPLOCK instead. */
|
|
oplock_request &= SAMBA_PRIVATE_OPLOCK_MASK;
|
|
}
|
|
}
|
|
|
|
/* Covert generic bits in the security descriptor. */
|
|
if (sd != NULL) {
|
|
security_acl_map_generic(sd->dacl, &file_generic_mapping);
|
|
security_acl_map_generic(sd->sacl, &file_generic_mapping);
|
|
}
|
|
|
|
/*
|
|
* If it's a request for a directory open, deal with it separately.
|
|
*/
|
|
|
|
if (create_options & FILE_DIRECTORY_FILE) {
|
|
|
|
if (create_options & FILE_NON_DIRECTORY_FILE) {
|
|
status = NT_STATUS_INVALID_PARAMETER;
|
|
goto fail;
|
|
}
|
|
|
|
/* Can't open a temp directory. IFS kit test. */
|
|
if (!(file_attributes & FILE_FLAG_POSIX_SEMANTICS) &&
|
|
(file_attributes & FILE_ATTRIBUTE_TEMPORARY)) {
|
|
status = NT_STATUS_INVALID_PARAMETER;
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* We will get a create directory here if the Win32
|
|
* app specified a security descriptor in the
|
|
* CreateDirectory() call.
|
|
*/
|
|
|
|
status = onefs_open_directory(
|
|
conn, /* conn */
|
|
req, /* req */
|
|
fname, /* fname */
|
|
access_mask, /* access_mask */
|
|
share_access, /* share_access */
|
|
create_disposition, /* create_disposition*/
|
|
create_options, /* create_options */
|
|
file_attributes, /* file_attributes */
|
|
sd, /* sd */
|
|
&fsp, /* result */
|
|
&info, /* pinfo */
|
|
&sbuf); /* psbuf */
|
|
} else {
|
|
|
|
/*
|
|
* Ordinary file case.
|
|
*/
|
|
|
|
status = file_new(req, conn, &fsp);
|
|
if(!NT_STATUS_IS_OK(status)) {
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* We're opening the stream element of a base_fsp
|
|
* we already opened. Set up the base_fsp pointer.
|
|
*/
|
|
if (base_fsp) {
|
|
fsp->base_fsp = base_fsp;
|
|
}
|
|
|
|
status = onefs_open_file_ntcreate(
|
|
conn, /* conn */
|
|
req, /* req */
|
|
fname, /* fname */
|
|
access_mask, /* access_mask */
|
|
share_access, /* share_access */
|
|
create_disposition, /* create_disposition*/
|
|
create_options, /* create_options */
|
|
file_attributes, /* file_attributes */
|
|
oplock_request, /* oplock_request */
|
|
sd, /* sd */
|
|
fsp, /* result */
|
|
&info, /* pinfo */
|
|
fsp_data, /* fsp_data */
|
|
&sbuf); /* psbuf */
|
|
|
|
if(!NT_STATUS_IS_OK(status)) {
|
|
file_free(req, fsp);
|
|
fsp = NULL;
|
|
}
|
|
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
|
|
|
|
/* A stream open never opens a directory */
|
|
|
|
if (base_fsp) {
|
|
status = NT_STATUS_FILE_IS_A_DIRECTORY;
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* Fail the open if it was explicitly a non-directory
|
|
* file.
|
|
*/
|
|
|
|
if (create_options & FILE_NON_DIRECTORY_FILE) {
|
|
status = NT_STATUS_FILE_IS_A_DIRECTORY;
|
|
goto fail;
|
|
}
|
|
|
|
create_options |= FILE_DIRECTORY_FILE;
|
|
|
|
status = onefs_open_directory(
|
|
conn, /* conn */
|
|
req, /* req */
|
|
fname, /* fname */
|
|
access_mask, /* access_mask */
|
|
share_access, /* share_access */
|
|
create_disposition, /* create_disposition*/
|
|
create_options, /* create_options */
|
|
file_attributes, /* file_attributes */
|
|
sd, /* sd */
|
|
&fsp, /* result */
|
|
&info, /* pinfo */
|
|
&sbuf); /* psbuf */
|
|
}
|
|
}
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto fail;
|
|
}
|
|
|
|
fsp->base_fsp = base_fsp;
|
|
|
|
SMB_ASSERT(fsp);
|
|
|
|
if ((ea_list != NULL) && (info == FILE_WAS_CREATED)) {
|
|
status = set_ea(conn, fsp, fname, ea_list);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
if (!fsp->is_directory && S_ISDIR(sbuf.st_ex_mode)) {
|
|
status = NT_STATUS_ACCESS_DENIED;
|
|
goto fail;
|
|
}
|
|
|
|
/* Save the requested allocation size. */
|
|
if ((info == FILE_WAS_CREATED) || (info == FILE_WAS_OVERWRITTEN)) {
|
|
if (allocation_size
|
|
&& (allocation_size > sbuf.st_ex_size)) {
|
|
fsp->initial_allocation_size = smb_roundup(
|
|
fsp->conn, allocation_size);
|
|
if (fsp->is_directory) {
|
|
/* Can't set allocation size on a directory. */
|
|
status = NT_STATUS_ACCESS_DENIED;
|
|
goto fail;
|
|
}
|
|
if (vfs_allocate_file_space(
|
|
fsp, fsp->initial_allocation_size) == -1) {
|
|
status = NT_STATUS_DISK_FULL;
|
|
goto fail;
|
|
}
|
|
} else {
|
|
fsp->initial_allocation_size = smb_roundup(
|
|
fsp->conn, (uint64_t)sbuf.st_ex_size);
|
|
}
|
|
}
|
|
|
|
DEBUG(10, ("onefs_create_file_unixpath: info=%d\n", info));
|
|
|
|
*result = fsp;
|
|
if (pinfo != NULL) {
|
|
*pinfo = info;
|
|
}
|
|
if (psbuf != NULL) {
|
|
if ((fsp->fh == NULL) || (fsp->fh->fd == -1)) {
|
|
*psbuf = sbuf;
|
|
}
|
|
else {
|
|
SMB_VFS_FSTAT(fsp, psbuf);
|
|
}
|
|
}
|
|
return NT_STATUS_OK;
|
|
|
|
fail:
|
|
DEBUG(10, ("onefs_create_file_unixpath: %s\n", nt_errstr(status)));
|
|
|
|
if (fsp != NULL) {
|
|
if (base_fsp && fsp->base_fsp == base_fsp) {
|
|
/*
|
|
* The close_file below will close
|
|
* fsp->base_fsp.
|
|
*/
|
|
base_fsp = NULL;
|
|
}
|
|
close_file(req, fsp, ERROR_CLOSE);
|
|
fsp = NULL;
|
|
}
|
|
if (base_fsp != NULL) {
|
|
close_file(req, base_fsp, ERROR_CLOSE);
|
|
base_fsp = NULL;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static void destroy_onefs_fsp_data(void *p_data)
|
|
{
|
|
struct onefs_fsp_data *fsp_data = (struct onefs_fsp_data *)p_data;
|
|
|
|
destroy_onefs_callback_record(fsp_data->oplock_callback_id);
|
|
}
|
|
|
|
/**
|
|
* SMB_VFS_CREATE_FILE interface to onefs.
|
|
*/
|
|
NTSTATUS onefs_create_file(vfs_handle_struct *handle,
|
|
struct smb_request *req,
|
|
uint16_t root_dir_fid,
|
|
const char *fname,
|
|
uint32_t create_file_flags,
|
|
uint32_t access_mask,
|
|
uint32_t share_access,
|
|
uint32_t create_disposition,
|
|
uint32_t create_options,
|
|
uint32_t file_attributes,
|
|
uint32_t oplock_request,
|
|
uint64_t allocation_size,
|
|
struct security_descriptor *sd,
|
|
struct ea_list *ea_list,
|
|
files_struct **result,
|
|
int *pinfo,
|
|
SMB_STRUCT_STAT *psbuf)
|
|
{
|
|
connection_struct *conn = handle->conn;
|
|
struct case_semantics_state *case_state = NULL;
|
|
struct onefs_fsp_data fsp_data = {};
|
|
SMB_STRUCT_STAT sbuf;
|
|
int info = FILE_WAS_OPENED;
|
|
files_struct *fsp = NULL;
|
|
NTSTATUS status;
|
|
|
|
DEBUG(10,("onefs_create_file: access_mask = 0x%x "
|
|
"file_attributes = 0x%x, share_access = 0x%x, "
|
|
"create_disposition = 0x%x create_options = 0x%x "
|
|
"oplock_request = 0x%x "
|
|
"root_dir_fid = 0x%x, ea_list = 0x%p, sd = 0x%p, "
|
|
"create_file_flags = 0x%x, fname = %s\n",
|
|
(unsigned int)access_mask,
|
|
(unsigned int)file_attributes,
|
|
(unsigned int)share_access,
|
|
(unsigned int)create_disposition,
|
|
(unsigned int)create_options,
|
|
(unsigned int)oplock_request,
|
|
(unsigned int)root_dir_fid,
|
|
ea_list, sd, create_file_flags, fname));
|
|
|
|
/* Get the file name if root_dir_fid was specified. */
|
|
if (root_dir_fid != 0) {
|
|
char *new_fname;
|
|
|
|
status = get_relative_fid_filename(conn, req, root_dir_fid,
|
|
fname, &new_fname);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto fail;
|
|
}
|
|
|
|
fname = new_fname;
|
|
}
|
|
|
|
/* Resolve the file name if this was a DFS pathname. */
|
|
if ((req != NULL) && (req->flags2 & FLAGS2_DFS_PATHNAMES)) {
|
|
char *resolved_fname;
|
|
|
|
status = resolve_dfspath(talloc_tos(), conn, true, fname,
|
|
&resolved_fname);
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
/*
|
|
* For PATH_NOT_COVERED we had
|
|
* reply_botherror(req, NT_STATUS_PATH_NOT_COVERED,
|
|
* ERRSRV, ERRbadpath);
|
|
* Need to fix in callers
|
|
*/
|
|
goto fail;
|
|
}
|
|
fname = resolved_fname;
|
|
}
|
|
|
|
/* Check if POSIX semantics are wanted. */
|
|
if (file_attributes & FILE_FLAG_POSIX_SEMANTICS) {
|
|
case_state = set_posix_case_semantics(talloc_tos(), conn);
|
|
}
|
|
|
|
/* Convert dos path to unix path if it hasn't already been done. */
|
|
if (create_file_flags & CFF_DOS_PATH) {
|
|
struct smb_filename *smb_fname = NULL;
|
|
char *converted_fname;
|
|
|
|
SET_STAT_INVALID(sbuf);
|
|
|
|
status = unix_convert(talloc_tos(), conn, fname, &smb_fname,
|
|
0);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto fail;
|
|
}
|
|
|
|
status = get_full_smb_filename(talloc_tos(), smb_fname,
|
|
&converted_fname);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
TALLOC_FREE(smb_fname);
|
|
goto fail;
|
|
}
|
|
|
|
sbuf = smb_fname->st;
|
|
fname = converted_fname;
|
|
TALLOC_FREE(smb_fname);
|
|
} else {
|
|
if (psbuf != NULL) {
|
|
sbuf = *psbuf;
|
|
} else {
|
|
if (SMB_VFS_STAT(conn, fname, &sbuf) == -1) {
|
|
SET_STAT_INVALID(sbuf);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
TALLOC_FREE(case_state);
|
|
|
|
/* All file access must go through check_name() */
|
|
status = check_name(conn, fname);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto fail;
|
|
}
|
|
|
|
status = onefs_create_file_unixpath(
|
|
conn, /* conn */
|
|
req, /* req */
|
|
fname, /* fname */
|
|
access_mask, /* access_mask */
|
|
share_access, /* share_access */
|
|
create_disposition, /* create_disposition*/
|
|
create_options, /* create_options */
|
|
file_attributes, /* file_attributes */
|
|
oplock_request, /* oplock_request */
|
|
allocation_size, /* allocation_size */
|
|
sd, /* sd */
|
|
ea_list, /* ea_list */
|
|
&fsp, /* result */
|
|
&info, /* pinfo */
|
|
&fsp_data, /* fsp_data */
|
|
&sbuf); /* psbuf */
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto fail;
|
|
}
|
|
|
|
DEBUG(10, ("onefs_create_file: info=%d\n", info));
|
|
|
|
/*
|
|
* Setup private onefs_fsp_data. Currently the private data struct is
|
|
* only used to store the oplock_callback_id so that when the file is
|
|
* closed, the onefs_callback_record can be properly cleaned up in the
|
|
* oplock_onefs sub-system.
|
|
*/
|
|
if (fsp) {
|
|
struct onefs_fsp_data *fsp_data_tmp = NULL;
|
|
fsp_data_tmp = (struct onefs_fsp_data *)
|
|
VFS_ADD_FSP_EXTENSION(handle, fsp, struct onefs_fsp_data,
|
|
&destroy_onefs_fsp_data);
|
|
|
|
if (fsp_data_tmp == NULL) {
|
|
status = NT_STATUS_NO_MEMORY;
|
|
goto fail;
|
|
}
|
|
|
|
*fsp_data_tmp = fsp_data;
|
|
}
|
|
|
|
*result = fsp;
|
|
if (pinfo != NULL) {
|
|
*pinfo = info;
|
|
}
|
|
if (psbuf != NULL) {
|
|
*psbuf = sbuf;
|
|
}
|
|
return NT_STATUS_OK;
|
|
|
|
fail:
|
|
DEBUG(10, ("onefs_create_file: %s\n", nt_errstr(status)));
|
|
|
|
if (fsp != NULL) {
|
|
close_file(req, fsp, ERROR_CLOSE);
|
|
fsp = NULL;
|
|
}
|
|
return status;
|
|
}
|