summaryrefslogtreecommitdiffstats
path: root/source3/smbd/fileio.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/smbd/fileio.c')
-rw-r--r--source3/smbd/fileio.c318
1 files changed, 318 insertions, 0 deletions
diff --git a/source3/smbd/fileio.c b/source3/smbd/fileio.c
new file mode 100644
index 0000000..ed62159
--- /dev/null
+++ b/source3/smbd/fileio.c
@@ -0,0 +1,318 @@
+/*
+ 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)
+{
+ off_t new_pos;
+ ssize_t ret = 0;
+ bool ok;
+
+ /* you can't read from print files */
+ if (fsp->print_file) {
+ errno = EBADF;
+ return -1;
+ }
+
+ ok = vfs_valid_pread_range(pos, n);
+ if (!ok) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ fh_set_pos(fsp->fh, 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));
+
+ new_pos = fh_get_pos(fsp->fh) + ret;
+ fh_set_pos(fsp->fh, new_pos);
+ fh_set_position_information(fsp->fh, new_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;
+ bool ok;
+
+ ok = vfs_valid_pwrite_range(pos, n);
+ if (!ok) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (n == 0) {
+ return 0;
+ }
+
+ fh_set_pos(fsp->fh, pos);
+ if (pos &&
+ lp_strict_allocate(SNUM(fsp->conn)) &&
+ !fsp->fsp_flags.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) {
+ off_t new_pos = fh_get_pos(fsp->fh) + ret;
+ fh_set_pos(fsp->fh, new_pos);
+
+/* 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)));
+
+ trigger_write_time_update_immediate(fsp);
+}
+
+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->fsp_flags.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->fsp_flags.update_write_time_on_close = true;
+
+ if (fsp->fsp_flags.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->fsp_flags.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->fsp_flags.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->fsp_flags.update_write_time_triggered = true;
+ fsp->fsp_flags.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;
+
+ trigger_write_time_update(fsp);
+
+ if (fsp->fsp_flags.modified) {
+ return;
+ }
+
+ fsp->fsp_flags.modified = true;
+
+ if (!(lp_store_dos_attributes(SNUM(fsp->conn)) ||
+ MAP_ARCHIVE(fsp->conn))) {
+ return;
+ }
+
+ dosmode = fdos_mode(fsp);
+ if (dosmode & FILE_ATTRIBUTE_ARCHIVE) {
+ 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->fsp_flags.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_get_io_fd(fsp) == -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;
+}