Exemplo n.º 1
0
static NTSTATUS cmd_pathfunc(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, const char **argv)
{
	int ret = -1;

	if (argc != 2) {
		printf("Usage: %s <path>\n", argv[0]);
		return NT_STATUS_OK;
	}

	if (strcmp("rmdir", argv[0]) == 0 ) {
		ret = SMB_VFS_RMDIR(vfs->conn, argv[1]);
	} else if (strcmp("unlink", argv[0]) == 0 ) {
		ret = SMB_VFS_UNLINK(vfs->conn, argv[1]);
	} else if (strcmp("chdir", argv[0]) == 0 ) {
		ret = SMB_VFS_CHDIR(vfs->conn, argv[1]);
	} else {
		printf("%s: error=%d (invalid function name!)\n", argv[0], errno);
		return NT_STATUS_UNSUCCESSFUL;
	}

	if (ret == -1) {
		printf("%s: error=%d (%s)\n", argv[0], errno, strerror(errno));
		return NT_STATUS_UNSUCCESSFUL;
	}

	printf("%s: ok\n", argv[0]);
	return NT_STATUS_OK;
}
Exemplo n.º 2
0
static NTSTATUS rmdir_internals(TALLOC_CTX *ctx, files_struct *fsp)
{
	connection_struct *conn = fsp->conn;
	struct smb_filename *smb_dname = fsp->fsp_name;
	int ret;

	SMB_ASSERT(!is_ntfs_stream_smb_fname(smb_dname));

	/* Might be a symlink. */
	if(SMB_VFS_LSTAT(conn, smb_dname) != 0) {
		return map_nt_error_from_unix(errno);
	}

	if (S_ISLNK(smb_dname->st.st_ex_mode)) {
		/* Is what it points to a directory ? */
		if(SMB_VFS_STAT(conn, smb_dname) != 0) {
			return map_nt_error_from_unix(errno);
		}
		if (!(S_ISDIR(smb_dname->st.st_ex_mode))) {
			return NT_STATUS_NOT_A_DIRECTORY;
		}
		ret = SMB_VFS_UNLINK(conn, smb_dname);
	} else {
		ret = SMB_VFS_RMDIR(conn, smb_dname->base_name);
	}
	if (ret == 0) {
		notify_fname(conn, NOTIFY_ACTION_REMOVED,
			     FILE_NOTIFY_CHANGE_DIR_NAME,
			     smb_dname->base_name);
		return NT_STATUS_OK;
	}

	if(((errno == ENOTEMPTY)||(errno == EEXIST)) && *lp_veto_files(talloc_tos(), SNUM(conn))) {
		/*
		 * Check to see if the only thing in this directory are
		 * vetoed files/directories. If so then delete them and
		 * retry. If we fail to delete any of them (and we *don't*
		 * do a recursive delete) then fail the rmdir.
		 */
		SMB_STRUCT_STAT st;
		const char *dname = NULL;
		char *talloced = NULL;
		long dirpos = 0;
		struct smb_Dir *dir_hnd = OpenDir(talloc_tos(), conn,
						  smb_dname->base_name, NULL,
						  0);

		if(dir_hnd == NULL) {
			errno = ENOTEMPTY;
			goto err;
		}

		while ((dname = ReadDirName(dir_hnd, &dirpos, &st,
					    &talloced)) != NULL) {
			if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) {
				TALLOC_FREE(talloced);
				continue;
			}
			if (!is_visible_file(conn, smb_dname->base_name, dname,
					     &st, false)) {
				TALLOC_FREE(talloced);
				continue;
			}
			if(!IS_VETO_PATH(conn, dname)) {
				TALLOC_FREE(dir_hnd);
				TALLOC_FREE(talloced);
				errno = ENOTEMPTY;
				goto err;
			}
			TALLOC_FREE(talloced);
		}

		/* We only have veto files/directories.
		 * Are we allowed to delete them ? */

		if(!lp_delete_veto_files(SNUM(conn))) {
			TALLOC_FREE(dir_hnd);
			errno = ENOTEMPTY;
			goto err;
		}

		/* Do a recursive delete. */
		RewindDir(dir_hnd,&dirpos);
		while ((dname = ReadDirName(dir_hnd, &dirpos, &st,
					    &talloced)) != NULL) {
			struct smb_filename *smb_dname_full = NULL;
			char *fullname = NULL;
			bool do_break = true;

			if (ISDOT(dname) || ISDOTDOT(dname)) {
				TALLOC_FREE(talloced);
				continue;
			}
			if (!is_visible_file(conn, smb_dname->base_name, dname,
					     &st, false)) {
				TALLOC_FREE(talloced);
				continue;
			}

			fullname = talloc_asprintf(ctx,
					"%s/%s",
					smb_dname->base_name,
					dname);

			if(!fullname) {
				errno = ENOMEM;
				goto err_break;
			}

			smb_dname_full = synthetic_smb_fname(
				talloc_tos(), fullname, NULL, NULL);
			if (smb_dname_full == NULL) {
				errno = ENOMEM;
				goto err_break;
			}

			if(SMB_VFS_LSTAT(conn, smb_dname_full) != 0) {
				goto err_break;
			}
			if(smb_dname_full->st.st_ex_mode & S_IFDIR) {
				if(!recursive_rmdir(ctx, conn,
						    smb_dname_full)) {
					goto err_break;
				}
				if(SMB_VFS_RMDIR(conn,
					smb_dname_full->base_name) != 0) {
					goto err_break;
				}
			} else if(SMB_VFS_UNLINK(conn, smb_dname_full) != 0) {
				goto err_break;
			}

			/* Successful iteration. */
			do_break = false;

		 err_break:
			TALLOC_FREE(fullname);
			TALLOC_FREE(smb_dname_full);
			TALLOC_FREE(talloced);
			if (do_break)
				break;
		}
		TALLOC_FREE(dir_hnd);
		/* Retry the rmdir */
		ret = SMB_VFS_RMDIR(conn, smb_dname->base_name);
	}

  err:

	if (ret != 0) {
		DEBUG(3,("rmdir_internals: couldn't remove directory %s : "
			 "%s\n", smb_fname_str_dbg(smb_dname),
			 strerror(errno)));
		return map_nt_error_from_unix(errno);
	}

	notify_fname(conn, NOTIFY_ACTION_REMOVED,
		     FILE_NOTIFY_CHANGE_DIR_NAME,
		     smb_dname->base_name);

	return NT_STATUS_OK;
}
Exemplo n.º 3
0
bool recursive_rmdir(TALLOC_CTX *ctx,
		     connection_struct *conn,
		     struct smb_filename *smb_dname)
{
	const char *dname = NULL;
	char *talloced = NULL;
	bool ret = True;
	long offset = 0;
	SMB_STRUCT_STAT st;
	struct smb_Dir *dir_hnd;

	SMB_ASSERT(!is_ntfs_stream_smb_fname(smb_dname));

	dir_hnd = OpenDir(talloc_tos(), conn, smb_dname->base_name, NULL, 0);
	if(dir_hnd == NULL)
		return False;

	while((dname = ReadDirName(dir_hnd, &offset, &st, &talloced))) {
		struct smb_filename *smb_dname_full = NULL;
		char *fullname = NULL;
		bool do_break = true;

		if (ISDOT(dname) || ISDOTDOT(dname)) {
			TALLOC_FREE(talloced);
			continue;
		}

		if (!is_visible_file(conn, smb_dname->base_name, dname, &st,
				     false)) {
			TALLOC_FREE(talloced);
			continue;
		}

		/* Construct the full name. */
		fullname = talloc_asprintf(ctx,
				"%s/%s",
				smb_dname->base_name,
				dname);
		if (!fullname) {
			errno = ENOMEM;
			goto err_break;
		}

		smb_dname_full = synthetic_smb_fname(talloc_tos(), fullname,
						     NULL, NULL);
		if (smb_dname_full == NULL) {
			errno = ENOMEM;
			goto err_break;
		}

		if(SMB_VFS_LSTAT(conn, smb_dname_full) != 0) {
			goto err_break;
		}

		if(smb_dname_full->st.st_ex_mode & S_IFDIR) {
			if(!recursive_rmdir(ctx, conn, smb_dname_full)) {
				goto err_break;
			}
			if(SMB_VFS_RMDIR(conn,
					 smb_dname_full->base_name) != 0) {
				goto err_break;
			}
		} else if(SMB_VFS_UNLINK(conn, smb_dname_full) != 0) {
			goto err_break;
		}

		/* Successful iteration. */
		do_break = false;

	 err_break:
		TALLOC_FREE(smb_dname_full);
		TALLOC_FREE(fullname);
		TALLOC_FREE(talloced);
		if (do_break) {
			ret = false;
			break;
		}
	}
	TALLOC_FREE(dir_hnd);
	return ret;
}
Exemplo n.º 4
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;
}
Exemplo n.º 5
0
NTSTATUS delete_all_streams(connection_struct *conn, const char *fname)
{
	struct stream_struct *stream_info = NULL;
	int i;
	unsigned int num_streams = 0;
	TALLOC_CTX *frame = talloc_stackframe();
	NTSTATUS status;

	status = vfs_streaminfo(conn, NULL, fname, talloc_tos(),
				&num_streams, &stream_info);

	if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
		DEBUG(10, ("no streams around\n"));
		TALLOC_FREE(frame);
		return NT_STATUS_OK;
	}

	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(10, ("vfs_streaminfo failed: %s\n",
			   nt_errstr(status)));
		goto fail;
	}

	DEBUG(10, ("delete_all_streams found %d streams\n",
		   num_streams));

	if (num_streams == 0) {
		TALLOC_FREE(frame);
		return NT_STATUS_OK;
	}

	for (i=0; i<num_streams; i++) {
		int res;
		struct smb_filename *smb_fname_stream;

		if (strequal(stream_info[i].name, "::$DATA")) {
			continue;
		}

		smb_fname_stream = synthetic_smb_fname(
			talloc_tos(), fname, stream_info[i].name, NULL);

		if (smb_fname_stream == NULL) {
			DEBUG(0, ("talloc_aprintf failed\n"));
			status = NT_STATUS_NO_MEMORY;
			goto fail;
		}

		res = SMB_VFS_UNLINK(conn, smb_fname_stream);

		if (res == -1) {
			status = map_nt_error_from_unix(errno);
			DEBUG(10, ("Could not delete stream %s: %s\n",
				   smb_fname_str_dbg(smb_fname_stream),
				   strerror(errno)));
			TALLOC_FREE(smb_fname_stream);
			break;
		}
		TALLOC_FREE(smb_fname_stream);
	}

 fail:
	TALLOC_FREE(frame);
	return status;
}
Exemplo n.º 6
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;
	struct share_mode_lock *lck;
	SMB_STRUCT_STAT sbuf;
	NTSTATUS status = NT_STATUS_OK;
	int ret;

	/*
	 * 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(NULL, fsp->dev, fsp->inode, NULL, NULL);

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

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

	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;
		}
		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->fsp_name));

	/* Become the user who requested the delete. */

	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);

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

	if (fsp->posix_open) {
		ret = SMB_VFS_LSTAT(conn,fsp->fsp_name,&sbuf);
	} else {
		ret = SMB_VFS_STAT(conn,fsp->fsp_name,&sbuf);
	}

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

	if(sbuf.st_dev != fsp->dev || sbuf.st_ino != fsp->inode) {
		DEBUG(5,("close_remove_share_mode: file %s. Delete on close "
			 "was set and dev and/or inode does not match\n",
			 fsp->fsp_name ));
		DEBUG(5,("close_remove_share_mode: file %s. stored dev = %x, "
			 "inode = %.0f stat dev = %x, inode = %.0f\n",
			 fsp->fsp_name,
			 (unsigned int)fsp->dev, (double)fsp->inode,
			 (unsigned int)sbuf.st_dev, (double)sbuf.st_ino ));
		/*
		 * Don't save the errno here, we ignore this error
		 */
		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->fsp_name, strerror(errno) ));

		status = map_nt_error_from_unix(errno);
	}

	notify_fname(conn, NOTIFY_ACTION_REMOVED,
		     FILE_NOTIFY_CHANGE_FILE_NAME,
		     fsp->fsp_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.
 	 */

	set_delete_on_close_lck(lck, False, NULL);

 done:

	/* unbecome user. */
	pop_sec_ctx();
	
	TALLOC_FREE(lck);
	return status;
}
Exemplo n.º 7
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;
}
Exemplo n.º 8
0
static int close_remove_share_mode(files_struct *fsp, enum file_close_type close_type)
{
	connection_struct *conn = fsp->conn;
	BOOL delete_file = False;
	struct share_mode_lock *lck;

	/*
	 * 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(NULL, fsp->dev, fsp->inode, NULL, NULL);

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

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

	delete_file = (lck->delete_on_close | lck->initial_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 */
		for (i=0; i<lck->num_share_modes; i++) {
			if (is_valid_share_mode_entry(&lck->share_modes[i])) {
				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) {
		SMB_STRUCT_STAT sbuf;

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

		/* Become the user who requested the delete. */

		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);

		/* We can only delete the file if the name we have
		   is still valid and hasn't been renamed. */
	
		if(SMB_VFS_STAT(conn,fsp->fsp_name,&sbuf) != 0) {
			DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set "
				"and stat failed with error %s\n",
				fsp->fsp_name, strerror(errno) ));
		} else {
			if(sbuf.st_dev != fsp->dev || sbuf.st_ino != fsp->inode) {
				DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set and "
					"dev and/or inode does not match\n",
					fsp->fsp_name ));
				DEBUG(5,("close_remove_share_mode: file %s. stored dev = %x, inode = %.0f "
					"stat dev = %x, inode = %.0f\n",
					fsp->fsp_name,
					(unsigned int)fsp->dev, (double)fsp->inode,
					(unsigned int)sbuf.st_dev, (double)sbuf.st_ino ));

			} 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->fsp_name, strerror(errno) ));
			}
		}
		/* unbecome user. */
		pop_sec_ctx();
	
		process_pending_change_notify_queue((time_t)0);
	}

	TALLOC_FREE(lck);
	return 0;
}
Exemplo n.º 9
0
static int close_normal_file(files_struct *fsp, BOOL normal_close)
{
	share_mode_entry *share_entry = NULL;
	size_t share_entry_count = 0;
	BOOL delete_on_close = False;
	connection_struct *conn = fsp->conn;
	int err = 0;
	int err1 = 0;

	remove_pending_lock_requests_by_fid(fsp);

	/*
	 * If we're flushing on a close we can get a write
	 * error here, we must remember this.
	 */

	if (close_filestruct(fsp) == -1)
		err1 = -1;

	if (fsp->print_file) {
		print_fsp_end(fsp, normal_close);
		file_free(fsp);
		return 0;
	}

	/*
	 * 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.
	 */

	lock_share_entry_fsp(fsp);

	if (fsp->delete_on_close) {

		/*
		 * Modify the share mode entry for all files open
		 * on this device and inode to tell other smbds we have
		 * changed the delete on close flag. The last closer will delete the file
		 * if flag is set.
		 */

		NTSTATUS status =set_delete_on_close_over_all(fsp, fsp->delete_on_close);
		if (NT_STATUS_V(status) !=  NT_STATUS_V(NT_STATUS_OK))
			DEBUG(0,("close_normal_file: failed to change delete on close flag for file %s\n",
				fsp->fsp_name ));
	}

	share_entry_count = del_share_mode(fsp, &share_entry);

	DEBUG(10,("close_normal_file: share_entry_count = %lu for file %s\n",
		(unsigned long)share_entry_count, fsp->fsp_name ));

	/*
	 * We delete on close if it's the last open, and the
	 * delete on close flag was set in the entry we just deleted.
	 */

	if ((share_entry_count == 0) && share_entry && 
			GET_DELETE_ON_CLOSE_FLAG(share_entry->share_mode) )
		delete_on_close = True;

	SAFE_FREE(share_entry);

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

	if (normal_close && delete_on_close) {
		DEBUG(5,("close_file: file %s. Delete on close was set - deleting file.\n",
			fsp->fsp_name));
		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_file: file %s. Delete on close was set and unlink failed \
with error %s\n", fsp->fsp_name, strerror(errno) ));
		}