Esempio n. 1
0
static int onefs_ntimes(vfs_handle_struct *handle, const char *fname,
			struct smb_file_time *ft)
{
	int flags = 0;
	struct timespec times[3];

	if (!null_timespec(ft->atime)) {
		flags |= VT_ATIME;
		times[0] = ft->atime;
		DEBUG(6,("**** onefs_ntimes: actime: %s.%d\n",
			time_to_asc(convert_timespec_to_time_t(ft->atime)),
			ft->atime.tv_nsec));
	}

	if (!null_timespec(ft->mtime)) {
		flags |= VT_MTIME;
		times[1] = ft->mtime;
		DEBUG(6,("**** onefs_ntimes: modtime: %s.%d\n",
			time_to_asc(convert_timespec_to_time_t(ft->mtime)),
			ft->mtime.tv_nsec));
	}

	if (!null_timespec(ft->create_time)) {
		flags |= VT_BTIME;
		times[2] = ft->create_time;
		DEBUG(6,("**** onefs_ntimes: createtime: %s.%d\n",
		   time_to_asc(convert_timespec_to_time_t(ft->create_time)),
		   ft->create_time.tv_nsec));
	}

	return onefs_vtimes_streams(handle, fname, flags, times);
}
Esempio n. 2
0
static NTSTATUS update_write_time_on_close(struct files_struct *fsp)
{
	struct smb_file_time ft;
	NTSTATUS status;
	struct share_mode_lock *lck = NULL;

	ZERO_STRUCT(ft);

	if (!fsp->update_write_time_on_close) {
		return NT_STATUS_OK;
	}

	if (null_timespec(fsp->close_write_time)) {
		fsp->close_write_time = timespec_current();
	}

	/* Ensure we have a valid stat struct for the source. */
	status = vfs_stat_fsp(fsp);
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	if (!VALID_STAT(fsp->fsp_name->st)) {
		/* if it doesn't seem to be a real file */
		return NT_STATUS_OK;
	}

	/* On close if we're changing the real file time we
	 * must update it in the open file db too. */
	(void)set_write_time(fsp->file_id, fsp->close_write_time);

	lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL, NULL);
	if (lck) {
		/* Close write times overwrite sticky write times
		   so we must replace any sticky write time here. */
		if (!null_timespec(lck->changed_write_time)) {
			(void)set_sticky_write_time(fsp->file_id, fsp->close_write_time);
		}
		TALLOC_FREE(lck);
	}

	ft.mtime = fsp->close_write_time;
	/* We must use NULL for the fsp handle here, as smb_set_file_time()
	   checks the fsp access_mask, which may not include FILE_WRITE_ATTRIBUTES.
	   As this is a close based update, we are not directly changing the
	   file attributes from a client call, but indirectly from a write. */
	status = smb_set_file_time(fsp->conn, NULL, fsp->fsp_name, &ft, false);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10,("update_write_time_on_close: smb_set_file_time "
			"on file %s returned %s\n",
			fsp_str_dbg(fsp),
			nt_errstr(status)));
		return status;
	}

	return status;
}
Esempio n. 3
0
static NTSTATUS update_write_time_on_close(struct files_struct *fsp)
{
    struct smb_file_time ft;
    NTSTATUS status;
    struct share_mode_lock *lck = NULL;

    ZERO_STRUCT(ft);

    if (!fsp->update_write_time_on_close) {
        return NT_STATUS_OK;
    }

    if (null_timespec(fsp->close_write_time)) {
        fsp->close_write_time = timespec_current();
    }

    /* Ensure we have a valid stat struct for the source. */
    status = vfs_stat_fsp(fsp);
    if (!NT_STATUS_IS_OK(status)) {
        return status;
    }

    if (!VALID_STAT(fsp->fsp_name->st)) {
        /* if it doesn't seem to be a real file */
        return NT_STATUS_OK;
    }

    /* On close if we're changing the real file time we
     * must update it in the open file db too. */
    (void)set_write_time(fsp->file_id, fsp->close_write_time);

    lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL, NULL);
    if (lck) {
        /* Close write times overwrite sticky write times
           so we must replace any sticky write time here. */
        if (!null_timespec(lck->changed_write_time)) {
            (void)set_sticky_write_time(fsp->file_id, fsp->close_write_time);
        }
        TALLOC_FREE(lck);
    }

    ft.mtime = fsp->close_write_time;
    status = smb_set_file_time(fsp->conn, fsp, fsp->fsp_name, &ft, false);
    if (!NT_STATUS_IS_OK(status)) {
        return status;
    }

    return status;
}
static void timespec_to_gpfs_time(struct timespec ts, gpfs_timestruc_t *gt,
				  int idx, int *flags)
{
	if (!null_timespec(ts)) {
		*flags |= 1 << idx;
		gt[idx].tv_sec = ts.tv_sec;
		gt[idx].tv_nsec = ts.tv_nsec;
		DEBUG(10, ("Setting GPFS time %d, flags 0x%x\n", idx, *flags));
	}
}
Esempio n. 5
0
bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
{
	if (null_timespec(mtime)) {
		return true;
	}

	fsp->write_time_forced = true;
	TALLOC_FREE(fsp->update_write_time_event);

	return set_sticky_write_time_path(fsp->file_id, mtime);
}
Esempio n. 6
0
void set_close_write_time(struct files_struct *fsp, struct timespec ts)
{
	DEBUG(6,("close_write_time: %s" , time_to_asc(convert_timespec_to_time_t(ts))));

	if (null_timespec(ts)) {
		return;
	}
	fsp->write_time_forced = false;
	fsp->update_write_time_on_close = true;
	fsp->close_write_time = ts;
}
Esempio n. 7
0
bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
{
	if (null_timespec(mtime)) {
		return true;
	}

	if (!set_sticky_write_time(fileid, mtime)) {
		return false;
	}

	return true;
}
Esempio n. 8
0
void set_close_write_time(struct files_struct *fsp, struct timespec ts)
{
    DEBUG(6,("close_write_time: %s" , time_to_asc(convert_timespec_to_time_t(ts))));

    if (null_timespec(ts)) {
        return;
    }
    /*
     * if the write time on close is explict set, then don't
     * need to fix it up to the value in the locking db
     */
    fsp->write_time_forced = false;

    fsp->update_write_time_on_close = true;
    fsp->close_write_time = ts;
}
Esempio n. 9
0
static NTSTATUS update_write_time_on_close(struct files_struct *fsp)
{
    SMB_STRUCT_STAT sbuf;
    struct timespec ts[2];
    NTSTATUS status;
    int ret = -1;

    ZERO_STRUCT(sbuf);
    ZERO_STRUCT(ts);

    if (!fsp->update_write_time_on_close) {
        return NT_STATUS_OK;
    }

    if (null_timespec(fsp->close_write_time)) {
        fsp->close_write_time = timespec_current();
    }

    /* Ensure we have a valid stat struct for the source. */
    if (fsp->fh->fd != -1) {
        ret = SMB_VFS_FSTAT(fsp, &sbuf);
    } else {
        if (fsp->posix_open) {
            ret = SMB_VFS_LSTAT(fsp->conn,fsp->fsp_name,&sbuf);
        } else {
            ret = SMB_VFS_STAT(fsp->conn,fsp->fsp_name,&sbuf);
        }
    }

    if (ret == -1) {
        return map_nt_error_from_unix(errno);
    }

    if (!VALID_STAT(sbuf)) {
        /* if it doesn't seem to be a real file */
        return NT_STATUS_OK;
    }

    ts[1] = fsp->close_write_time;
    status = smb_set_file_time(fsp->conn, fsp, fsp->fsp_name,
                               &sbuf, ts, true);
    if (!NT_STATUS_IS_OK(status)) {
        return status;
    }

    return NT_STATUS_OK;
}
Esempio n. 10
0
BOOL set_filetime(connection_struct *conn, const char *fname,
		const struct timespec mtime)
{
	struct timespec ts[2];

	if (null_timespec(mtime)) {
		return(True);
	}

	ts[1] = mtime; /* mtime. */
	ts[0] = ts[1]; /* atime. */

	if (file_ntimes(conn, fname, ts)) {
		DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
		return False;
	}

	notify_fname(conn, NOTIFY_ACTION_MODIFIED,
		     FILE_NOTIFY_CHANGE_LAST_WRITE, fname);
  
	return True;
}
Esempio n. 11
0
void get_file_infos(struct file_id id,
		    uint32_t name_hash,
		    bool *delete_on_close,
		    struct timespec *write_time)
{
	struct share_mode_lock *lck;

	if (delete_on_close) {
		*delete_on_close = false;
	}

	if (write_time) {
		ZERO_STRUCTP(write_time);
	}

	if (!(lck = fetch_share_mode_unlocked(talloc_tos(), id))) {
		return;
	}

	if (delete_on_close) {
		*delete_on_close = is_delete_on_close_set(lck, name_hash);
	}

	if (write_time) {
		struct timespec wt;

		wt = lck->data->changed_write_time;
		if (null_timespec(wt)) {
			wt = lck->data->old_write_time;
		}

		*write_time = wt;
	}

	TALLOC_FREE(lck);
}
Esempio n. 12
0
static NTSTATUS update_write_time_on_close(struct files_struct *fsp)
{
	struct smb_file_time ft;
	NTSTATUS status;
	struct share_mode_lock *lck = NULL;

	ZERO_STRUCT(ft);

	if (!fsp->update_write_time_on_close) {
		return NT_STATUS_OK;
	}

	if (null_timespec(fsp->close_write_time)) {
		fsp->close_write_time = timespec_current();
	}

	/* Ensure we have a valid stat struct for the source. */
	status = vfs_stat_fsp(fsp);
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	if (!VALID_STAT(fsp->fsp_name->st)) {
		/* if it doesn't seem to be a real file */
		return NT_STATUS_OK;
	}

	/*
	 * get_existing_share_mode_lock() isn't really the right
	 * call here, as we're being called after
	 * close_remove_share_mode() inside close_normal_file()
	 * so it's quite normal to not have an existing share
	 * mode here. However, get_share_mode_lock() doesn't
	 * work because that will create a new share mode if
	 * one doesn't exist - so stick with this call (just
	 * ignore any error we get if the share mode doesn't
	 * exist.
	 */

	lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
	if (lck) {
		/* On close if we're changing the real file time we
		 * must update it in the open file db too. */
		(void)set_write_time(fsp->file_id, fsp->close_write_time);

		/* Close write times overwrite sticky write times
		   so we must replace any sticky write time here. */
		if (!null_timespec(lck->data->changed_write_time)) {
			(void)set_sticky_write_time(fsp->file_id, fsp->close_write_time);
		}
		TALLOC_FREE(lck);
	}

	ft.mtime = fsp->close_write_time;
	/* As this is a close based update, we are not directly changing the
	   file attributes from a client call, but indirectly from a write. */
	status = smb_set_file_time(fsp->conn, fsp, fsp->fsp_name, &ft, false);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10,("update_write_time_on_close: smb_set_file_time "
			"on file %s returned %s\n",
			fsp_str_dbg(fsp),
			nt_errstr(status)));
		return status;
	}

	return status;
}
Esempio n. 13
0
static NTSTATUS close_remove_share_mode(files_struct *fsp,
					enum file_close_type close_type)
{
	connection_struct *conn = fsp->conn;
	struct server_id self = messaging_server_id(conn->sconn->msg_ctx);
	bool delete_file = false;
	bool changed_user = false;
	struct share_mode_lock *lck = NULL;
	NTSTATUS status = NT_STATUS_OK;
	NTSTATUS tmp_status;
	struct file_id id;
	const struct security_unix_token *del_token = NULL;
	const struct security_token *del_nt_token = NULL;
	bool got_tokens = false;
	bool normal_close;
	int ret_flock, retries = 1;

	/* Ensure any pending write time updates are done. */
	if (fsp->update_write_time_event) {
		update_write_time_handler(fsp->conn->sconn->ev_ctx,
					fsp->update_write_time_event,
					timeval_current(),
					(void *)fsp);
	}

	/*
	 * Lock the share entries, and determine if we should delete
	 * on close. If so delete whilst the lock is still in effect.
	 * This prevents race conditions with the file being created. JRA.
	 */

	lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
	if (lck == NULL) {
		DEBUG(0, ("close_remove_share_mode: Could not get share mode "
			  "lock for file %s\n", fsp_str_dbg(fsp)));
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (fsp->write_time_forced) {
		DEBUG(10,("close_remove_share_mode: write time forced "
			"for file %s\n",
			fsp_str_dbg(fsp)));
		set_close_write_time(fsp, lck->data->changed_write_time);
	} else if (fsp->update_write_time_on_close) {
		/* Someone had a pending write. */
		if (null_timespec(fsp->close_write_time)) {
			DEBUG(10,("close_remove_share_mode: update to current time "
				"for file %s\n",
				fsp_str_dbg(fsp)));
			/* Update to current time due to "normal" write. */
			set_close_write_time(fsp, timespec_current());
		} else {
			DEBUG(10,("close_remove_share_mode: write time pending "
				"for file %s\n",
				fsp_str_dbg(fsp)));
			/* Update to time set on close call. */
			set_close_write_time(fsp, fsp->close_write_time);
		}
	}

	if (fsp->initial_delete_on_close &&
			!is_delete_on_close_set(lck, fsp->name_hash)) {
		bool became_user = False;

		/* Initial delete on close was set and no one else
		 * wrote a real delete on close. */

		if (get_current_vuid(conn) != fsp->vuid) {
			become_user(conn, fsp->vuid);
			became_user = True;
		}
		fsp->delete_on_close = true;
		set_delete_on_close_lck(fsp, lck,
				get_current_nttok(conn),
				get_current_utok(conn));
		if (became_user) {
			unbecome_user();
		}
	}

	delete_file = is_delete_on_close_set(lck, fsp->name_hash);

	if (delete_file) {
		int i;
		/* See if others still have the file open via this pathname.
		   If this is the case, then don't delete. If all opens are
		   POSIX delete now. */
		for (i=0; i<lck->data->num_share_modes; i++) {
			struct share_mode_entry *e = &lck->data->share_modes[i];

			if (!is_valid_share_mode_entry(e)) {
				continue;
			}
			if (e->name_hash != fsp->name_hash) {
				continue;
			}
			if ((fsp->posix_flags & FSP_POSIX_FLAGS_OPEN)
			    && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) {
				continue;
			}
			if (serverid_equal(&self, &e->pid) &&
			    (e->share_file_id == fsp->fh->gen_id)) {
				continue;
			}
			if (share_mode_stale_pid(lck->data, i)) {
				continue;
			}
			delete_file = False;
			break;
		}
	}

	/*
	 * NT can set delete_on_close of the last open
	 * reference to a file.
	 */

	normal_close = (close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE);

	if (!normal_close || !delete_file) {
		status = NT_STATUS_OK;
		goto done;
	}

	/*
	 * Ok, we have to delete the file
	 */

	DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set "
		 "- deleting file.\n", fsp_str_dbg(fsp)));

	/*
	 * Don't try to update the write time when we delete the file
	 */
	fsp->update_write_time_on_close = false;

	got_tokens = get_delete_on_close_token(lck, fsp->name_hash,
					&del_nt_token, &del_token);
	SMB_ASSERT(got_tokens);

	if (!unix_token_equal(del_token, get_current_utok(conn))) {
		/* Become the user who requested the delete. */

		DEBUG(5,("close_remove_share_mode: file %s. "
			"Change user to uid %u\n",
			fsp_str_dbg(fsp),
			(unsigned int)del_token->uid));

		if (!push_sec_ctx()) {
			smb_panic("close_remove_share_mode: file %s. failed to push "
				  "sec_ctx.\n");
		}

		set_sec_ctx(del_token->uid,
			    del_token->gid,
			    del_token->ngroups,
			    del_token->groups,
			    del_nt_token);

		changed_user = true;
	}

	/* We can only delete the file if the name we have is still valid and
	   hasn't been renamed. */

	tmp_status = vfs_stat_fsp(fsp);
	if (!NT_STATUS_IS_OK(tmp_status)) {
		DEBUG(5,("close_remove_share_mode: file %s. Delete on close "
			 "was set and stat failed with error %s\n",
			 fsp_str_dbg(fsp), nt_errstr(tmp_status)));
		/*
		 * Don't save the errno here, we ignore this error
		 */
		goto done;
	}

	id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);

	if (!file_id_equal(&fsp->file_id, &id)) {
		DEBUG(5,("close_remove_share_mode: file %s. Delete on close "
			 "was set and dev and/or inode does not match\n",
			 fsp_str_dbg(fsp)));
		DEBUG(5,("close_remove_share_mode: file %s. stored file_id %s, "
			 "stat file_id %s\n",
			 fsp_str_dbg(fsp),
			 file_id_string_tos(&fsp->file_id),
			 file_id_string_tos(&id)));
		/*
		 * Don't save the errno here, we ignore this error
		 */
		goto done;
	}

	if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
	    && !is_ntfs_stream_smb_fname(fsp->fsp_name)) {

		status = delete_all_streams(conn, fsp->fsp_name->base_name);

		if (!NT_STATUS_IS_OK(status)) {
			DEBUG(5, ("delete_all_streams failed: %s\n",
				  nt_errstr(status)));
			goto done;
		}
	}

retry_delete:
	/* temporary files with delete on close set will not be deleted on a
	 * cifs share using a netapp backend since they are opened with
	 * read + write access mask.
	 * close the file to allow the delete.
	 */
	if (fsp->can_write && !S_ISDIR(fsp->fsp_name->st.st_ex_mode) &&
	    fsp->fh->ref_count == 1 && retries) {
		status = fd_close(fsp);
		if (!NT_STATUS_IS_OK(status)) {
			DEBUG(3, ("close_remove_share_mode: Error %s closing %s\n",
				nt_errstr(status),
				smb_fname_str_dbg(fsp->fsp_name)));
			goto skip_retry;
		}
		if (SMB_VFS_UNLINK(conn, fsp->fsp_name) != 0) {
			/*
			* This call can potentially fail as another smbd may
			* have had the file open with delete on close set and
			* deleted it when its last reference to this file
			* went away. Hence we log this but not at debug level
			* zero.
			*/

			DEBUG(5,("close_remove_share_mode: file %s. Delete on close "
				 "was set and unlink failed with error %s\n",
				 fsp_str_dbg(fsp), strerror(errno)));

			status = map_nt_error_from_unix(errno);
			retries = 0;
			goto retry_delete;
		}
	} else {
		if (SMB_VFS_UNLINK(conn, fsp->fsp_name) != 0) {
			/*
			 * This call can potentially fail as another smbd may
			 * have had the file open with delete on close set and
			 * deleted it when its last reference to this file
			 * went away. Hence we log this but not at debug level
			 * zero.
			 */

			DEBUG(5,("close_remove_share_mode: file %s. Delete on close "
				 "was set and unlink failed with error %s\n",
				 fsp_str_dbg(fsp), strerror(errno)));

			status = map_nt_error_from_unix(errno);
		}
	}

	/* As we now have POSIX opens which can unlink
 	 * with other open files we may have taken
 	 * this code path with more than one share mode
 	 * entry - ensure we only delete once by resetting
 	 * the delete on close flag. JRA.
 	 */

skip_retry:
	fsp->delete_on_close = false;
	reset_delete_on_close_lck(fsp, lck);

 done:

	if (changed_user) {
		/* unbecome user. */
		pop_sec_ctx();
	}

	/* remove filesystem sharemodes */
	ret_flock = SMB_VFS_KERNEL_FLOCK(fsp, 0, 0);
	if (ret_flock == -1) {
		DEBUG(2, ("close_remove_share_mode: removing kernel flock for "
					"%s failed: %s\n", fsp_str_dbg(fsp),
					strerror(errno)));
	}

	if (!del_share_mode(lck, fsp)) {
		DEBUG(0, ("close_remove_share_mode: Could not delete share "
			  "entry for file %s\n", fsp_str_dbg(fsp)));
	}

	TALLOC_FREE(lck);

	if (delete_file) {
		/*
		 * Do the notification after we released the share
		 * mode lock. Inside notify_fname we take out another
		 * tdb lock. With ctdb also accessing our databases,
		 * this can lead to deadlocks. Putting this notify
		 * after the TALLOC_FREE(lck) above we avoid locking
		 * two records simultaneously. Notifies are async and
		 * informational only, so calling the notify_fname
		 * without holding the share mode lock should not do
		 * any harm.
		 */
		notify_fname(conn, NOTIFY_ACTION_REMOVED,
			     FILE_NOTIFY_CHANGE_FILE_NAME,
			     fsp->fsp_name->base_name);
	}

	return status;
}
Esempio n. 14
0
static NTSTATUS close_normal_file(files_struct *fsp, enum file_close_type close_type)
{
	NTSTATUS status = NT_STATUS_OK;
	NTSTATUS saved_status1 = NT_STATUS_OK;
	NTSTATUS saved_status2 = NT_STATUS_OK;
	NTSTATUS saved_status3 = NT_STATUS_OK;
	connection_struct *conn = fsp->conn;

	if (fsp->aio_write_behind) {
		/*
	 	 * If we're finishing write behind on a close we can get a write
		 * error here, we must remember this.
		 */
		int ret = wait_for_aio_completion(fsp);
		if (ret) {
			saved_status1 = map_nt_error_from_unix(ret);
		}
	} else {
		cancel_aio_by_fsp(fsp);
	}
 
	/*
	 * If we're flushing on a close we can get a write
	 * error here, we must remember this.
	 */

	saved_status2 = close_filestruct(fsp);

	if (fsp->print_file) {
		print_fsp_end(fsp, close_type);
		file_free(fsp);
		return NT_STATUS_OK;
	}

	/* If this is an old DOS or FCB open and we have multiple opens on
	   the same handle we only have one share mode. Ensure we only remove
	   the share mode on the last close. */

	if (fsp->fh->ref_count == 1) {
		/* Should we return on error here... ? */
		saved_status3 = close_remove_share_mode(fsp, close_type);
	}

	if(fsp->oplock_type) {
		release_file_oplock(fsp);
	}

	locking_close_file(fsp);

	status = fd_close(conn, fsp);

	/* check for magic scripts */
	if (close_type == NORMAL_CLOSE) {
		check_magic(fsp,conn);
	}

	/*
	 * Ensure pending modtime is set after close.
	 */

	if (fsp->pending_modtime_owner && !null_timespec(fsp->pending_modtime)) {
		set_filetime(conn, fsp->fsp_name, fsp->pending_modtime);
	} else if (!null_timespec(fsp->last_write_time)) {
		set_filetime(conn, fsp->fsp_name, fsp->last_write_time);
	}

	if (NT_STATUS_IS_OK(status)) {
		if (!NT_STATUS_IS_OK(saved_status1)) {
			status = saved_status1;
		} else if (!NT_STATUS_IS_OK(saved_status2)) {
			status = saved_status2;
		} else if (!NT_STATUS_IS_OK(saved_status3)) {
			status = saved_status3;
		}
	}

	DEBUG(2,("%s closed file %s (numopen=%d) %s\n",
		conn->user,fsp->fsp_name,
		conn->num_files_open,
		nt_errstr(status) ));

	file_free(fsp);
	return status;
}
Esempio n. 15
0
static NTSTATUS close_remove_share_mode(files_struct *fsp,
                                        enum file_close_type close_type)
{
    connection_struct *conn = fsp->conn;
    bool delete_file = false;
    bool changed_user = false;
    struct share_mode_lock *lck = NULL;
    NTSTATUS status = NT_STATUS_OK;
    NTSTATUS tmp_status;
    struct file_id id;

    /* Ensure any pending write time updates are done. */
    if (fsp->update_write_time_event) {
        update_write_time_handler(smbd_event_context(),
                                  fsp->update_write_time_event,
                                  timeval_current(),
                                  (void *)fsp);
    }

    /*
     * Lock the share entries, and determine if we should delete
     * on close. If so delete whilst the lock is still in effect.
     * This prevents race conditions with the file being created. JRA.
     */

    lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL,
                              NULL);

    if (lck == NULL) {
        DEBUG(0, ("close_remove_share_mode: Could not get share mode "
                  "lock for file %s\n", fsp_str_dbg(fsp)));
        status = NT_STATUS_INVALID_PARAMETER;
        goto done;
    }

    if (fsp->write_time_forced) {
        DEBUG(10,("close_remove_share_mode: write time forced "
                  "for file %s\n",
                  fsp_str_dbg(fsp)));
        set_close_write_time(fsp, lck->changed_write_time);
    } else if (fsp->update_write_time_on_close) {
        /* Someone had a pending write. */
        if (null_timespec(fsp->close_write_time)) {
            DEBUG(10,("close_remove_share_mode: update to current time "
                      "for file %s\n",
                      fsp_str_dbg(fsp)));
            /* Update to current time due to "normal" write. */
            set_close_write_time(fsp, timespec_current());
        } else {
            DEBUG(10,("close_remove_share_mode: write time pending "
                      "for file %s\n",
                      fsp_str_dbg(fsp)));
            /* Update to time set on close call. */
            set_close_write_time(fsp, fsp->close_write_time);
        }
    }

    if (!del_share_mode(lck, fsp)) {
        DEBUG(0, ("close_remove_share_mode: Could not delete share "
                  "entry for file %s\n",
                  fsp_str_dbg(fsp)));
    }

    if (fsp->initial_delete_on_close && (lck->delete_token == NULL)) {
        bool became_user = False;

        /* Initial delete on close was set and no one else
         * wrote a real delete on close. */

        if (current_user.vuid != fsp->vuid) {
            become_user(conn, fsp->vuid);
            became_user = True;
        }
        fsp->delete_on_close = true;
        set_delete_on_close_lck(lck, True, &current_user.ut);
        if (became_user) {
            unbecome_user();
        }
    }

    delete_file = lck->delete_on_close;

    if (delete_file) {
        int i;
        /* See if others still have the file open. If this is the
         * case, then don't delete. If all opens are POSIX delete now. */
        for (i=0; i<lck->num_share_modes; i++) {
            struct share_mode_entry *e = &lck->share_modes[i];
            if (is_valid_share_mode_entry(e)) {
                if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) {
                    continue;
                }
                delete_file = False;
                break;
            }
        }
    }

    /* Notify any deferred opens waiting on this close. */
    notify_deferred_opens(lck);
    reply_to_oplock_break_requests(fsp);

    /*
     * NT can set delete_on_close of the last open
     * reference to a file.
     */

    if (!(close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE)
            || !delete_file
            || (lck->delete_token == NULL)) {
        TALLOC_FREE(lck);
        return NT_STATUS_OK;
    }

    /*
     * Ok, we have to delete the file
     */

    DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set "
             "- deleting file.\n", fsp_str_dbg(fsp)));

    /*
     * Don't try to update the write time when we delete the file
     */
    fsp->update_write_time_on_close = false;

    if (!unix_token_equal(lck->delete_token, &current_user.ut)) {
        /* Become the user who requested the delete. */

        DEBUG(5,("close_remove_share_mode: file %s. "
                 "Change user to uid %u\n",
                 fsp_str_dbg(fsp),
                 (unsigned int)lck->delete_token->uid));

        if (!push_sec_ctx()) {
            smb_panic("close_remove_share_mode: file %s. failed to push "
                      "sec_ctx.\n");
        }

        set_sec_ctx(lck->delete_token->uid,
                    lck->delete_token->gid,
                    lck->delete_token->ngroups,
                    lck->delete_token->groups,
                    NULL);

        changed_user = true;
    }

    /* We can only delete the file if the name we have is still valid and
       hasn't been renamed. */

    tmp_status = vfs_stat_fsp(fsp);
    if (!NT_STATUS_IS_OK(tmp_status)) {
        DEBUG(5,("close_remove_share_mode: file %s. Delete on close "
                 "was set and stat failed with error %s\n",
                 fsp_str_dbg(fsp), nt_errstr(tmp_status)));
        /*
         * Don't save the errno here, we ignore this error
         */
        goto done;
    }

    id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);

    if (!file_id_equal(&fsp->file_id, &id)) {
        DEBUG(5,("close_remove_share_mode: file %s. Delete on close "
                 "was set and dev and/or inode does not match\n",
                 fsp_str_dbg(fsp)));
        DEBUG(5,("close_remove_share_mode: file %s. stored file_id %s, "
                 "stat file_id %s\n",
                 fsp_str_dbg(fsp),
                 file_id_string_tos(&fsp->file_id),
                 file_id_string_tos(&id)));
        /*
         * Don't save the errno here, we ignore this error
         */
        goto done;
    }

    if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
            && !is_ntfs_stream_smb_fname(fsp->fsp_name)) {

        status = delete_all_streams(conn, fsp->fsp_name->base_name);

        if (!NT_STATUS_IS_OK(status)) {
            DEBUG(5, ("delete_all_streams failed: %s\n",
                      nt_errstr(status)));
            goto done;
        }
    }


    if (SMB_VFS_UNLINK(conn, fsp->fsp_name) != 0) {
        /*
         * This call can potentially fail as another smbd may
         * have had the file open with delete on close set and
         * deleted it when its last reference to this file
         * went away. Hence we log this but not at debug level
         * zero.
         */

        DEBUG(5,("close_remove_share_mode: file %s. Delete on close "
                 "was set and unlink failed with error %s\n",
                 fsp_str_dbg(fsp), strerror(errno)));

        status = map_nt_error_from_unix(errno);
    }

    notify_fname(conn, NOTIFY_ACTION_REMOVED,
                 FILE_NOTIFY_CHANGE_FILE_NAME,
                 fsp->fsp_name->base_name);

    /* As we now have POSIX opens which can unlink
     * with other open files we may have taken
     * this code path with more than one share mode
     * entry - ensure we only delete once by resetting
     * the delete on close flag. JRA.
     */

    fsp->delete_on_close = false;
    set_delete_on_close_lck(lck, False, NULL);

done:

    if (changed_user) {
        /* unbecome user. */
        pop_sec_ctx();
    }

    TALLOC_FREE(lck);
    return status;
}
Esempio n. 16
0
File: durable.c Progetto: hef/samba
NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
					const DATA_BLOB old_cookie,
					TALLOC_CTX *mem_ctx,
					DATA_BLOB *new_cookie)
{
	struct connection_struct *conn = fsp->conn;
	NTSTATUS status;
	enum ndr_err_code ndr_err;
	struct vfs_default_durable_cookie cookie;
	DATA_BLOB new_cookie_blob = data_blob_null;
	struct share_mode_lock *lck;
	bool ok;

	*new_cookie = data_blob_null;

	ZERO_STRUCT(cookie);

	ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
			(ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		status = ndr_map_error2ntstatus(ndr_err);
		return status;
	}

	if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (!file_id_equal(&fsp->file_id, &cookie.id)) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (!BATCH_OPLOCK_TYPE(fsp->oplock_type)) {
		return NT_STATUS_NOT_SUPPORTED;
	}

	/*
	 * For now let it be simple and do not keep
	 * delete on close files durable open
	 */
	if (fsp->initial_delete_on_close) {
		return NT_STATUS_NOT_SUPPORTED;
	}
	if (fsp->delete_on_close) {
		return NT_STATUS_NOT_SUPPORTED;
	}

	if (!VALID_STAT(fsp->fsp_name->st)) {
		return NT_STATUS_NOT_SUPPORTED;
	}

	if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
		return NT_STATUS_NOT_SUPPORTED;
	}

	/* Ensure any pending write time updates are done. */
	if (fsp->update_write_time_event) {
		update_write_time_handler(fsp->conn->sconn->ev_ctx,
					fsp->update_write_time_event,
					timeval_current(),
					(void *)fsp);
	}

	/*
	 * The above checks are done in mark_share_mode_disconnected() too
	 * but we want to avoid getting the lock if possible
	 */
	lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
	if (lck != NULL) {
		struct smb_file_time ft;

		ZERO_STRUCT(ft);

		if (fsp->write_time_forced) {
			ft.mtime = lck->data->changed_write_time;
		} else if (fsp->update_write_time_on_close) {
			if (null_timespec(fsp->close_write_time)) {
				ft.mtime = timespec_current();
			} else {
				ft.mtime = fsp->close_write_time;
			}
		}

		if (!null_timespec(ft.mtime)) {
			round_timespec(conn->ts_res, &ft.mtime);
			file_ntimes(conn, fsp->fsp_name, &ft);
		}

		ok = mark_share_mode_disconnected(lck, fsp);
		if (!ok) {
			TALLOC_FREE(lck);
		}
	}
	if (lck != NULL) {
		ok = brl_mark_disconnected(fsp);
		if (!ok) {
			TALLOC_FREE(lck);
		}
	}
	if (lck == NULL) {
		return NT_STATUS_NOT_SUPPORTED;
	}
	TALLOC_FREE(lck);

	status = vfs_stat_fsp(fsp);
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	ZERO_STRUCT(cookie);
	cookie.allow_reconnect = true;
	cookie.id = fsp->file_id;
	cookie.servicepath = conn->connectpath;
	cookie.base_name = fsp->fsp_name->base_name;
	cookie.initial_allocation_size = fsp->initial_allocation_size;
	cookie.position_information = fsp->fh->position_information;
	cookie.update_write_time_triggered = fsp->update_write_time_triggered;
	cookie.update_write_time_on_close = fsp->update_write_time_on_close;
	cookie.write_time_forced = fsp->write_time_forced;
	cookie.close_write_time = fsp->close_write_time;

	cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev;
	cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino;
	cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode;
	cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink;
	cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid;
	cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid;
	cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev;
	cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size;
	cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime;
	cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime;
	cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime;
	cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime;
	cookie.stat_info.st_ex_calculated_birthtime = fsp->fsp_name->st.st_ex_calculated_birthtime;
	cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize;
	cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks;
	cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags;
	cookie.stat_info.st_ex_mask = fsp->fsp_name->st.st_ex_mask;

	ndr_err = ndr_push_struct_blob(&new_cookie_blob, mem_ctx, &cookie,
			(ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		status = ndr_map_error2ntstatus(ndr_err);
		return status;
	}

	status = fd_close(fsp);
	if (!NT_STATUS_IS_OK(status)) {
		data_blob_free(&new_cookie_blob);
		return status;
	}

	*new_cookie = new_cookie_blob;
	return NT_STATUS_OK;
}