mirror of
https://github.com/samba-team/samba.git
synced 2025-02-02 09:47:23 +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 commit is contained in:
parent
9d93af713f
commit
932e337db8
@ -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