mirror of
https://github.com/samba-team/samba.git
synced 2025-03-27 22:50:26 +03:00
r12213: Final fix for #3303 - send rename messages to smbd's
that have open file handles to allow them to correctly implement delete on close. There is a further correctness fix I'm intending to add to this to cope with different share paths, but not right now... Jeremy. (This used to be commit 932e337db8788e75344e1c7cf1ef009d090cb039)
This commit is contained in:
parent
7d2771e758
commit
ab7a4f7e8e
@ -68,6 +68,7 @@
|
||||
#define MSG_SMB_ASYNC_LEVEL2_BREAK 3008
|
||||
#define MSG_SMB_OPEN_RETRY 3009
|
||||
#define MSG_SMB_KERNEL_BREAK 3010
|
||||
#define MSG_SMB_FILE_RENAME 3011
|
||||
|
||||
/* winbind messages */
|
||||
#define MSG_WINBIND_FINISHED 4001
|
||||
|
@ -1459,6 +1459,12 @@ struct kernel_oplock_message {
|
||||
unsigned long file_id;
|
||||
};
|
||||
|
||||
struct file_renamed_message {
|
||||
SMB_DEV_T dev;
|
||||
SMB_INO_T inode;
|
||||
char names[1]; /* A variable area containing sharepath and filename. */
|
||||
};
|
||||
|
||||
/*
|
||||
* On the wire return values for oplock types.
|
||||
*/
|
||||
|
@ -642,14 +642,24 @@ struct share_mode_lock *get_share_mode_lock(TALLOC_CTX *mem_ctx,
|
||||
|
||||
/*******************************************************************
|
||||
Sets the service name and filename for rename.
|
||||
At this point we should emit "rename" smbd messages to all
|
||||
interested process id's.
|
||||
At this point we emit "file renamed" messages to all
|
||||
process id's that have this file open.
|
||||
Based on an initial code idea from SATOH Fumiyasu <fumiya@samba.gr.jp>
|
||||
********************************************************************/
|
||||
|
||||
BOOL rename_share_filename(struct share_mode_lock *lck,
|
||||
const char *servicepath,
|
||||
const char *newname)
|
||||
{
|
||||
struct file_renamed_message *frm = NULL;
|
||||
size_t sp_len;
|
||||
size_t fn_len;
|
||||
size_t msg_len;
|
||||
int i;
|
||||
|
||||
DEBUG(10, ("rename_share_filename: servicepath %s newname %s\n",
|
||||
servicepath, newname));
|
||||
|
||||
/*
|
||||
* rename_internal_fsp() and rename_internals() add './' to
|
||||
* head of newname if newname does not contain a '/'.
|
||||
@ -658,13 +668,53 @@ BOOL rename_share_filename(struct share_mode_lock *lck,
|
||||
newname += 2;
|
||||
}
|
||||
|
||||
lck->filename = talloc_strdup(lck, newname);
|
||||
lck->servicepath = talloc_strdup(lck, servicepath);
|
||||
lck->filename = talloc_strdup(lck, newname);
|
||||
if (lck->filename == NULL || lck->servicepath == NULL) {
|
||||
DEBUG(0, ("rename_share_filename: talloc failed\n"));
|
||||
return False;
|
||||
}
|
||||
lck->modified = True;
|
||||
|
||||
sp_len = strlen(lck->servicepath);
|
||||
fn_len = strlen(lck->filename);
|
||||
|
||||
msg_len = sizeof(*frm) + sp_len + 1 + fn_len + 1;
|
||||
|
||||
/* Set up the name changed message. */
|
||||
frm = TALLOC(lck, msg_len);
|
||||
if (!frm) {
|
||||
return False;
|
||||
}
|
||||
frm->dev = lck->dev;
|
||||
frm->inode = lck->ino;
|
||||
|
||||
DEBUG(10,("rename_share_filename: msg_len = %d\n", msg_len ));
|
||||
|
||||
safe_strcpy(&frm->names[0], lck->servicepath, sp_len);
|
||||
safe_strcpy(&frm->names[sp_len + 1], lck->filename, fn_len);
|
||||
|
||||
/* Send the messages. */
|
||||
for (i=0; i<lck->num_share_modes; i++) {
|
||||
struct share_mode_entry *se = &lck->share_modes[i];
|
||||
if (!is_valid_share_mode_entry(se)) {
|
||||
continue;
|
||||
}
|
||||
/* But not to ourselves... */
|
||||
if (procid_is_me(&se->pid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DEBUG(10,("rename_share_filename: sending rename message to pid %u "
|
||||
"dev %x, inode %.0f sharepath %s newname %s\n",
|
||||
(unsigned int)procid_to_pid(&se->pid),
|
||||
(unsigned int)frm->dev, (double)frm->inode,
|
||||
lck->servicepath, lck->filename ));
|
||||
|
||||
message_send_pid(se->pid, MSG_SMB_FILE_RENAME,
|
||||
frm, msg_len, True);
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
@ -227,20 +227,43 @@ static int close_normal_file(files_struct *fsp, BOOL normal_close)
|
||||
*/
|
||||
|
||||
if (normal_close && delete_file) {
|
||||
SMB_STRUCT_STAT sbuf;
|
||||
|
||||
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) ));
|
||||
/* 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_file: 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_file: file %s. Delete on close was set and "
|
||||
"dev and/or inode does not match\n",
|
||||
fsp->fsp_name ));
|
||||
DEBUG(5,("close_file: 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_file: file %s. Delete on close was set "
|
||||
"and unlink failed with error %s\n",
|
||||
fsp->fsp_name, strerror(errno) ));
|
||||
}
|
||||
process_pending_change_notify_queue((time_t)0);
|
||||
}
|
||||
process_pending_change_notify_queue((time_t)0);
|
||||
}
|
||||
|
||||
talloc_free(lck);
|
||||
|
@ -2042,3 +2042,50 @@ files_struct *open_file_stat(connection_struct *conn, char *fname,
|
||||
|
||||
return fsp;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Receive notification that one of our open files has been renamed by another
|
||||
smbd process.
|
||||
****************************************************************************/
|
||||
|
||||
void msg_file_was_renamed(int msg_type, struct process_id src, void *buf, size_t len)
|
||||
{
|
||||
files_struct *fsp;
|
||||
struct file_renamed_message *frm = (struct file_renamed_message *)buf;
|
||||
const char *sharepath;
|
||||
const char *newname;
|
||||
size_t sp_len;
|
||||
|
||||
if (buf == NULL || len < sizeof(*frm)) {
|
||||
DEBUG(0, ("msg_file_was_renamed: Got invalid msg len %d\n", (int)len));
|
||||
return;
|
||||
}
|
||||
|
||||
sharepath = &frm->names[0];
|
||||
newname = sharepath + strlen(sharepath) + 1;
|
||||
sp_len = strlen(sharepath);
|
||||
|
||||
DEBUG(10,("msg_file_was_renamed: Got rename message for sharepath %s, new name %s, "
|
||||
"dev %x, inode %.0f\n",
|
||||
sharepath, newname, (unsigned int)frm->dev, (double)frm->inode ));
|
||||
|
||||
for(fsp = file_find_di_first(frm->dev, frm->inode); fsp; fsp = file_find_di_next(fsp)) {
|
||||
if (memcmp(fsp->conn->connectpath, sharepath, sp_len) == 0) {
|
||||
DEBUG(10,("msg_file_was_renamed: renaming file fnum %d from %s -> %s\n",
|
||||
fsp->fnum, fsp->fsp_name, newname ));
|
||||
string_set(&fsp->fsp_name, newname);
|
||||
} else {
|
||||
/* TODO. JRA. */
|
||||
/* Now we have the complete path we can work out if this is
|
||||
actually within this share and adjust newname accordingly. */
|
||||
DEBUG(10,("msg_file_was_renamed: share mismatch (sharepath %s "
|
||||
"not sharepath %s) "
|
||||
"fnum %d from %s -> %s\n",
|
||||
fsp->conn->connectpath,
|
||||
sharepath,
|
||||
fsp->fnum,
|
||||
fsp->fsp_name,
|
||||
newname ));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4082,13 +4082,15 @@ static BOOL resolve_wildcards(const char *name1, char *name2)
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Ensure open files have their names updates.
|
||||
Ensure open files have their names updated. Updated to notify other smbd's
|
||||
asynchronously.
|
||||
****************************************************************************/
|
||||
|
||||
static void rename_open_files(connection_struct *conn, SMB_DEV_T dev, SMB_INO_T inode, char *newname)
|
||||
static void rename_open_files(connection_struct *conn, SMB_DEV_T dev, SMB_INO_T inode, const char *newname)
|
||||
{
|
||||
files_struct *fsp;
|
||||
BOOL did_rename = False;
|
||||
struct share_mode_lock *lck = NULL;
|
||||
|
||||
for(fsp = file_find_di_first(dev, inode); fsp; fsp = file_find_di_next(fsp)) {
|
||||
DEBUG(10,("rename_open_files: renaming file fnum %d (dev = %x, inode = %.0f) from %s -> %s\n",
|
||||
@ -4098,9 +4100,24 @@ static void rename_open_files(connection_struct *conn, SMB_DEV_T dev, SMB_INO_T
|
||||
did_rename = True;
|
||||
}
|
||||
|
||||
if (!did_rename)
|
||||
if (!did_rename) {
|
||||
DEBUG(10,("rename_open_files: no open files on dev %x, inode %.0f for %s\n",
|
||||
(unsigned int)dev, (double)inode, newname ));
|
||||
}
|
||||
|
||||
/* Notify all remote smbd's. */
|
||||
lck = get_share_mode_lock(NULL, dev, inode, NULL, NULL);
|
||||
if (lck == NULL) {
|
||||
DEBUG(5,("rename_open_files: Could not get share mode lock for file %s\n",
|
||||
fsp->fsp_name));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Change the stored filename. */
|
||||
rename_share_filename(lck, conn->connectpath, newname);
|
||||
|
||||
/* Send messages to all smbd's (not ourself) that the name has changed. */
|
||||
talloc_free(lck);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -4238,10 +4255,11 @@ NTSTATUS rename_internals_fsp(connection_struct *conn, files_struct *fsp, char *
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (errno == ENOTDIR || errno == EISDIR)
|
||||
if (errno == ENOTDIR || errno == EISDIR) {
|
||||
error = NT_STATUS_OBJECT_NAME_COLLISION;
|
||||
else
|
||||
} else {
|
||||
error = map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
DEBUG(3,("rename_internals_fsp: Error %s rename %s -> %s\n",
|
||||
nt_errstr(error), fsp->fsp_name,newname));
|
||||
|
@ -330,6 +330,7 @@ static BOOL open_sockets_smbd(BOOL is_daemon, BOOL interactive, const char *smb_
|
||||
message_register(MSG_SMB_SAM_SYNC, msg_sam_sync);
|
||||
message_register(MSG_SMB_SAM_REPL, msg_sam_repl);
|
||||
message_register(MSG_SHUTDOWN, msg_exit_server);
|
||||
message_register(MSG_SMB_FILE_RENAME, msg_file_was_renamed);
|
||||
|
||||
/* now accept incoming connections - forking a new process
|
||||
for each incoming connection */
|
||||
|
@ -44,7 +44,7 @@ void set_conn_connectpath(connection_struct *conn, const pstring connectpath)
|
||||
while (*s == '/') {
|
||||
s++;
|
||||
}
|
||||
if ((d != destname) && (*s != '\0')) {
|
||||
if ((d > destname + 1) && (*s != '\0')) {
|
||||
*d++ = '/';
|
||||
}
|
||||
start_of_name_component = True;
|
||||
@ -119,6 +119,9 @@ void set_conn_connectpath(connection_struct *conn, const pstring connectpath)
|
||||
*(d-1) = '\0';
|
||||
}
|
||||
|
||||
DEBUG(10,("set_conn_connectpath: service %s, connectpath = %s\n",
|
||||
lp_servicename(SNUM(conn)), destname ));
|
||||
|
||||
string_set(&conn->connectpath, destname);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user