1
0
mirror of https://github.com/samba-team/samba.git synced 2025-03-09 08:58:35 +03:00
Ralph Boehme 69691dd0cd smbd: fix handling of sentinel timestamp values
This implements two core changes:

* use NTTIME instead of struct timespec at the database layer

* use struct timespec { .tv_nsec = SAMBA_UTIME_OMIT } as special sentinel
  value in smbd when processing timestamps

Using NTTIME at the database layer is only done to avoid storing the special
struct timespec sentinel values on disk. Instead, with NTTIME the sentinel value
for an "unset" timestamp is just 0 on-disk.

The NTTIME value of 0 gets translated by nt_time_to_full_timespec() to the
struct timespec sentinel value { .tv_nsec = SAMBA_UTIME_OMIT }.

The function is_omit_timespec() can be used to check this.

Beside nt_time_to_full_timespec(), there are various other new time conversion
functions with *full* in their name that can be used to safely convert between
different types with the changed sentinel value.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=7771

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
2019-12-06 00:17:36 +00:00

310 lines
7.5 KiB
C

/*
Unix SMB/Netbios implementation.
Version 1.9.
read/write to a files_struct
Copyright (C) Andrew Tridgell 1992-1998
Copyright (C) Jeremy Allison 2000-2002. - write cache.
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 "printing.h"
#include "smbd/smbd.h"
#include "smbd/globals.h"
#include "smbprofile.h"
/****************************************************************************
Read from a file.
****************************************************************************/
ssize_t read_file(files_struct *fsp,char *data,off_t pos,size_t n)
{
ssize_t ret = 0;
/* you can't read from print files */
if (fsp->print_file) {
errno = EBADF;
return -1;
}
fsp->fh->pos = pos;
if (n > 0) {
ret = SMB_VFS_PREAD(fsp,data,n,pos);
if (ret == -1) {
return -1;
}
}
DEBUG(10,("read_file (%s): pos = %.0f, size = %lu, returned %lu\n",
fsp_str_dbg(fsp), (double)pos, (unsigned long)n, (long)ret));
fsp->fh->pos += ret;
fsp->fh->position_information = fsp->fh->pos;
return(ret);
}
/****************************************************************************
*Really* write to a file.
****************************************************************************/
static ssize_t real_write_file(struct smb_request *req,
files_struct *fsp,
const char *data,
off_t pos,
size_t n)
{
ssize_t ret;
fsp->fh->pos = pos;
if (pos && lp_strict_allocate(SNUM(fsp->conn) &&
!fsp->is_sparse)) {
if (vfs_fill_sparse(fsp, pos) == -1) {
return -1;
}
}
ret = vfs_pwrite_data(req, fsp, data, n, pos);
DEBUG(10,("real_write_file (%s): pos = %.0f, size = %lu, returned %ld\n",
fsp_str_dbg(fsp), (double)pos, (unsigned long)n, (long)ret));
if (ret != -1) {
fsp->fh->pos += ret;
/* Yes - this is correct - writes don't update this. JRA. */
/* Found by Samba4 tests. */
#if 0
fsp->position_information = fsp->pos;
#endif
}
return ret;
}
void fsp_flush_write_time_update(struct files_struct *fsp)
{
/*
* Note this won't expect any impersonation!
* So don't call any SMB_VFS operations here!
*/
DEBUG(5, ("Update write time on %s\n", fsp_str_dbg(fsp)));
/* change the write time in the open file db. */
(void)set_write_time(fsp->file_id, timespec_current());
/* And notify. */
notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
FILE_NOTIFY_CHANGE_LAST_WRITE, fsp->fsp_name->base_name);
/* Remove the timed event handler. */
TALLOC_FREE(fsp->update_write_time_event);
}
static void update_write_time_handler(struct tevent_context *ctx,
struct tevent_timer *te,
struct timeval now,
void *private_data)
{
files_struct *fsp = (files_struct *)private_data;
fsp_flush_write_time_update(fsp);
}
/*********************************************************
Schedule a write time update for WRITE_TIME_UPDATE_USEC_DELAY
in the future.
*********************************************************/
void trigger_write_time_update(struct files_struct *fsp)
{
int delay;
if (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) {
/* Don't use delayed writes on POSIX files. */
return;
}
if (fsp->write_time_forced) {
/* No point - "sticky" write times
* in effect.
*/
return;
}
/* We need to remember someone did a write
* and update to current time on close. */
fsp->update_write_time_on_close = true;
if (fsp->update_write_time_triggered) {
/*
* We only update the write time after 2 seconds
* on the first normal write. After that
* no other writes affect this until close.
*/
return;
}
fsp->update_write_time_triggered = true;
delay = lp_parm_int(SNUM(fsp->conn),
"smbd", "writetimeupdatedelay",
WRITE_TIME_UPDATE_USEC_DELAY);
DEBUG(5, ("Update write time %d usec later on %s\n",
delay, fsp_str_dbg(fsp)));
/* trigger the update 2 seconds later */
fsp->update_write_time_event =
tevent_add_timer(fsp->conn->sconn->ev_ctx, NULL,
timeval_current_ofs_usec(delay),
update_write_time_handler, fsp);
}
void trigger_write_time_update_immediate(struct files_struct *fsp)
{
struct smb_file_time ft;
init_smb_file_time(&ft);
if (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) {
/* Don't use delayed writes on POSIX files. */
return;
}
if (fsp->write_time_forced) {
/*
* No point - "sticky" write times
* in effect.
*/
return;
}
TALLOC_FREE(fsp->update_write_time_event);
DEBUG(5, ("Update write time immediate on %s\n",
fsp_str_dbg(fsp)));
/* After an immediate update, reset the trigger. */
fsp->update_write_time_triggered = true;
fsp->update_write_time_on_close = false;
ft.mtime = timespec_current();
/* Update the time in the open file db. */
(void)set_write_time(fsp->file_id, ft.mtime);
/* Now set on disk - takes care of notify. */
(void)smb_set_file_time(fsp->conn, fsp, fsp->fsp_name, &ft, false);
}
void mark_file_modified(files_struct *fsp)
{
int dosmode;
if (fsp->modified) {
return;
}
fsp->modified = true;
if (SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st) != 0) {
return;
}
trigger_write_time_update(fsp);
if (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) {
return;
}
if (!(lp_store_dos_attributes(SNUM(fsp->conn)) ||
MAP_ARCHIVE(fsp->conn))) {
return;
}
dosmode = dos_mode(fsp->conn, fsp->fsp_name);
if (IS_DOS_ARCHIVE(dosmode)) {
return;
}
file_set_dosmode(fsp->conn, fsp->fsp_name,
dosmode | FILE_ATTRIBUTE_ARCHIVE, NULL, false);
}
/****************************************************************************
Write to a file.
****************************************************************************/
ssize_t write_file(struct smb_request *req,
files_struct *fsp,
const char *data,
off_t pos,
size_t n)
{
ssize_t total_written = 0;
if (fsp->print_file) {
uint32_t t;
int ret;
ret = print_spool_write(fsp, data, n, pos, &t);
if (ret) {
errno = ret;
return -1;
}
return t;
}
if (!fsp->can_write) {
errno = EPERM;
return -1;
}
mark_file_modified(fsp);
/*
* If this file is level II oplocked then we need
* to grab the shared memory lock and inform all
* other files with a level II lock that they need
* to flush their read caches. We keep the lock over
* the shared memory area whilst doing this.
*/
/* This should actually be improved to span the write. */
contend_level2_oplocks_begin(fsp, LEVEL2_CONTEND_WRITE);
contend_level2_oplocks_end(fsp, LEVEL2_CONTEND_WRITE);
total_written = real_write_file(req, fsp, data, pos, n);
return total_written;
}
/*******************************************************************
sync a file
********************************************************************/
NTSTATUS sync_file(connection_struct *conn, files_struct *fsp, bool write_through)
{
if (fsp->fh->fd == -1)
return NT_STATUS_INVALID_HANDLE;
if (lp_strict_sync(SNUM(conn)) &&
(lp_sync_always(SNUM(conn)) || write_through)) {
int ret;
ret = smb_vfs_fsync_sync(fsp);
if (ret == -1) {
return map_nt_error_from_unix(errno);
}
}
return NT_STATUS_OK;
}