[dovecot-cvs] dovecot/src/lib-storage/index/maildir maildir-sync.c, 1.96, 1.97

tss at dovecot.org tss at dovecot.org
Fri Mar 23 19:20:27 EET 2007


Update of /var/lib/cvs/dovecot/src/lib-storage/index/maildir
In directory talvi:/tmp/cvs-serv17354

Modified Files:
	maildir-sync.c 
Log Message:
If duplicate links are found, rename() one over the other to get rid of
it.



Index: maildir-sync.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/lib-storage/index/maildir/maildir-sync.c,v
retrieving revision 1.96
retrieving revision 1.97
diff -u -d -r1.96 -r1.97
--- maildir-sync.c	23 Mar 2007 16:38:56 -0000	1.96
+++ maildir-sync.c	23 Mar 2007 17:20:24 -0000	1.97
@@ -692,7 +692,8 @@
 	struct maildir_mailbox *mbox = ctx->mbox;
 	const char *existing_fname, *existing_path;
 	const char *new_fname, *old_path, *new_path;
-	struct stat st, st2;
+	struct stat ex_st, old_st, ex2_st, old2_st;
+	bool delete_old;
 	int ret = 0;
 
 	existing_fname =
@@ -705,18 +706,69 @@
 	existing_path = t_strconcat(dir, "/", existing_fname, NULL);
 	old_path = t_strconcat(dir, "/", old_fname, NULL);
 
-	if (stat(existing_path, &st) < 0 ||
-	    stat(old_path, &st2) < 0) {
+	if (stat(existing_path, &ex_st) < 0 ||
+	    stat(old_path, &old_st) < 0) {
 		/* most likely the files just don't exist anymore.
 		   don't really care about other errors much. */
 		t_pop();
 		return 0;
 	}
-	if (st.st_ino == st2.st_ino && CMP_DEV_T(st.st_dev, st2.st_dev)) {
+	if (ex_st.st_ino == old_st.st_ino &&
+	    CMP_DEV_T(ex_st.st_dev, old_st.st_dev)) {
 		/* files are the same. this means either a race condition
-		   between stat() calls, or someone has started link()ing the
-		   files. either way there's no data loss if we just leave it
-		   there. */
+		   between stat() calls, or that the files were link()ed.
+
+		   if the files really were link()ed, we want to get rid of
+		   them. however we can't just go directly rename() them
+		   because if this was a race between stat()s, we could reverse
+		   a flag change done by another process. so, stat again
+		   and hope that there wasn't another race condition changing
+		   the flag back.. */
+		if (!(stat(existing_path, &ex2_st) == 0 &&
+		      stat(old_path, &old2_st) == 0 &&
+#ifdef HAVE_STAT_TV_NSEC
+		      ex_st.st_ctim.tv_nsec == ex2_st.st_ctim.tv_nsec &&
+		      old_st.st_ctim.tv_nsec == old2_st.st_ctim.tv_nsec &&
+#endif
+		      ex_st.st_ctime == ex2_st.st_ctime &&
+		      old_st.st_ctime == old2_st.st_ctime)) {
+			/* changed again */
+			t_pop();
+			return 0;
+		}
+
+		/* don't unlink(), if this is a race condition after
+		   all we really don't want to delete the message
+		   accidentally.
+
+		   the ctime checks are only meant to help with stat() race
+		   conditions. the inode is shared between links, so there's
+		   no way to know which filename was created later. */
+		if (old_st.st_ctime != ex_st.st_ctime)
+			delete_old = old_st.st_ctime < ex_st.st_ctime;
+		else {
+#ifdef HAVE_STAT_TV_NSEC
+			delete_old = old_st.st_ctim.tv_nsec <
+				ex_st.st_ctim.tv_nsec;
+#else
+			delete_old = TRUE;
+#endif
+		}
+		if (delete_old)
+			new_path = existing_path;
+		else {
+			new_path = old_path;
+			old_path = existing_path;
+		}
+		ret = rename(old_path, new_path);
+		if (ret == 0) {
+			i_warning("Fixed a duplicate link: %s -> %s",
+				  old_path, new_path);
+		} else if (ret < 0 && errno != ENOENT) {
+			mail_storage_set_critical(STORAGE(mbox->storage),
+						  "rename(%s, %s) failed: %m",
+						  old_path, new_path);
+		}
 		t_pop();
 		return 0;
 	}
@@ -724,10 +776,9 @@
 	new_fname = maildir_generate_tmp_filename(&ioloop_timeval);
 	new_path = t_strconcat(mbox->path, "/new/", new_fname, NULL);
 
-	if (rename(old_path, new_path) == 0) {
-		i_warning("Fixed duplicate in %s: %s -> %s",
-			  mbox->path, old_fname, new_fname);
-	} else if (errno != ENOENT) {
+	if (rename(old_path, new_path) == 0)
+		i_warning("Fixed a duplicate: %s -> %s", old_fname, new_fname);
+	else if (errno != ENOENT) {
 		mail_storage_set_critical(STORAGE(mbox->storage),
 			"rename(%s, %s) failed: %m", old_path, new_path);
 		ret = -1;



More information about the dovecot-cvs mailing list