summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2021-02-09 09:25:43 +0200
committerSergey Poznyakoff <gray@gnu.org>2021-02-09 10:00:49 +0200
commit07ee21c2db558106c8537600698db75e51ac1090 (patch)
tree30b1813007efb38a040eb977958163e0a8462cf0
parent000c989d079613acbca5986bbc0ff8a1e896245c (diff)
downloadmailutils-07ee21c2db558106c8537600698db75e51ac1090.tar.gz
mailutils-07ee21c2db558106c8537600698db75e51ac1090.tar.bz2
mbox: fix expunging in a read-only directory
This fixes debian bug 980042. Re: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=980042#16 * libproto/mbox/mboxrd.c (mboxrd_flush_unlocked): If temporary file cannot be created in the directory of the mailbox because of EACCES, create it elsewhere and copy back to the mailbox when ready. Also, make sure that the mode and ownership of the mailbox are preserved, no matter what temporary file was used. * libproto/mbox/tests/rospool.at: New test. * libproto/mbox/tests/Makefile.am: Add new test. * libproto/mbox/tests/testsuite.at: Add new test.
-rw-r--r--libproto/mbox/mboxrd.c178
-rw-r--r--libproto/mbox/tests/Makefile.am1
-rw-r--r--libproto/mbox/tests/rospool.at370
-rw-r--r--libproto/mbox/tests/testsuite.at1
4 files changed, 510 insertions, 40 deletions
diff --git a/libproto/mbox/mboxrd.c b/libproto/mbox/mboxrd.c
index 86ab33064..68fff3a97 100644
--- a/libproto/mbox/mboxrd.c
+++ b/libproto/mbox/mboxrd.c
@@ -1496,6 +1496,54 @@ mboxrd_flush_temp (struct mu_mboxrd_flush_tracker *trk,
return mu_stream_flush (tempstr);
}
+/*
+ * Copy the temporary mailbox stream TEMPSTR to the mailbox referred to by
+ * the tracker TRK.
+ */
+static inline int
+mboxrd_copyback (struct mu_mboxrd_flush_tracker *trk, mu_stream_t tempstr)
+{
+ int rc;
+ mu_stream_t mbx_stream = trk->dmp->mailbox->stream;
+ mu_off_t size;
+
+ rc = mu_stream_seek (tempstr, 0, MU_SEEK_SET, NULL);
+ if (rc)
+ {
+ mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
+ ("%s: can't rewind temporary file: %s",
+ __func__, mu_strerror (rc)));
+ return rc;
+ }
+
+ rc = mu_stream_seek (mbx_stream, 0, MU_SEEK_SET, NULL);
+ if (rc)
+ {
+ mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
+ ("%s: can't rewind mailbox %s: %s",
+ __func__, trk->dmp->name, mu_strerror (rc)));
+ return rc;
+ }
+
+ rc = mu_stream_copy (mbx_stream, tempstr, 0, &size);
+ if (rc)
+ {
+ mu_error (_("copying back to mailbox %s failed: %s"),
+ trk->dmp->name, mu_strerror (rc));
+ return rc;
+ }
+ rc = mu_stream_truncate (mbx_stream, size);
+ if (rc)
+ {
+ mu_error (_("cannot truncate mailbox stream: %s"),
+ mu_stream_strerror (mbx_stream, rc));
+ return rc;
+ }
+
+ mboxrd_tracker_sync (trk);
+ return 0;
+}
+
/* Flush the mailbox described by the tracker TRK to the stream TEMPSTR.
EXPUNGE is 1 if the MU_ATTRIBUTE_DELETED attribute is to be honored.
Assumes that simultaneous access to the mailbox has been blocked.
@@ -1616,13 +1664,26 @@ mboxrd_flush_unlocked (struct mu_mboxrd_flush_tracker *trk, int mode)
return ENOMEM;
}
rc = mu_tempfile (&hints, MU_TEMPFILE_TMPDIR, &tempfd, &tempname);
- if (rc)
+ if (rc == 0)
{
- free (hints.tmpdir);
- return rc;
+ rc = mu_fd_stream_create (&tempstr, tempname, tempfd,
+ MU_STREAM_RDWR|MU_STREAM_SEEK);
+ }
+ else if (rc == EACCES)
+ {
+ /*
+ * Mail spool directory is not writable for the user. Fall
+ * back to using temporary stream located elsewhere. When
+ * ready, it will be copied back to the mailbox.
+ *
+ * Reset the tempname to NULL to instruct the code below
+ * which approach to take.
+ */
+ tempname = NULL;
+
+ rc = mu_temp_file_stream_create (&tempstr, NULL, 0);
}
- rc = mu_fd_stream_create (&tempstr, tempname, tempfd,
- MU_STREAM_RDWR|MU_STREAM_SEEK);
+
if (rc)
{
free (hints.tmpdir);
@@ -1632,58 +1693,95 @@ mboxrd_flush_unlocked (struct mu_mboxrd_flush_tracker *trk, int mode)
}
rc = mboxrd_flush_temp (trk, dirty, tempstr, mode == FLUSH_EXPUNGE);
- mu_stream_unref (tempstr);
if (rc == 0)
{
- /* Rename mailbox to temporary copy */
- char *backup = mu_tempname (hints.tmpdir);
- rc = rename (dmp->name, backup);
- if (rc)
- {
- mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
- ("%s:%s: failed to rename to backup file %s: %s",
- __func__, dmp->name, tempname,
- mu_strerror (rc)));
- unlink (backup);
- }
- else
+ if (tempname)
{
- rc = rename (tempname, dmp->name);
- if (rc == 0)
+ /* Mail spool is writable. Rename the temporary copy back
+ to mailbox */
+ char *backup;
+ struct stat st;
+
+ if (stat (dmp->name, &st))
{
- /* Success. Synchronize internal data with the counter. */
- mboxrd_tracker_sync (trk);
- mu_stream_destroy (&dmp->mailbox->stream);
- rc = mboxrd_mailbox_init_stream (dmp);
+ mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
+ ("%s:%s: stat failed: %s",
+ __func__, dmp->name, strerror (errno)));
+ rc = errno;
}
else
{
- int rc1;
- mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
- ("%s: failed to rename temporary file %s %s: %s",
- __func__, tempname, dmp->name,
- mu_strerror (rc)));
- rc1 = rename (backup, dmp->name);
- if (rc1)
+ mu_stream_flush (tempstr);
+ backup = mu_tempname (hints.tmpdir);
+ rc = rename (dmp->name, backup);
+ if (rc)
+ {
+ mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
+ ("%s:%s: failed to rename to backup file %s: %s",
+ __func__, dmp->name, tempname,
+ mu_strerror (rc)));
+ unlink (backup);
+ }
+ else
+ {
+ rc = rename (tempname, dmp->name);
+ if (rc == 0)
+ {
+ /* Success. Synchronize internal data with the
+ counter. */
+ mboxrd_tracker_sync (trk);
+ mu_stream_destroy (&dmp->mailbox->stream);
+ rc = mboxrd_mailbox_init_stream (dmp);
+ if (rc == 0)
+ {
+ if (chmod (dmp->name, st.st_mode))
+ mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
+ ("%s:%s: chmod failed: %s",
+ __func__, dmp->name,
+ strerror (errno)));
+ if (chown (dmp->name, st.st_uid, st.st_gid))
+ mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
+ ("%s:%s: chown failed: %s",
+ __func__, dmp->name,
+ strerror (errno)));
+ }
+ }
+ else
+ {
+ int rc1;
+ mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
+ ("%s: failed to rename temporary file %s %s: %s",
+ __func__, tempname, dmp->name,
+ mu_strerror (rc)));
+ rc1 = rename (backup, dmp->name);
+ if (rc1)
+ {
+ mu_error (_("failed to restore %s from backup %s: %s"),
+ dmp->name, backup, mu_strerror (rc1));
+ mu_error (_("backup left in %s"), backup);
+ free (backup);
+ backup = NULL;
+ }
+ }
+ }
+
+ if (backup)
{
- mu_error (_("failed to restore %s from backup %s: %s"),
- dmp->name, backup, mu_strerror (rc1));
- mu_error (_("backup left in %s"), backup);
+ unlink (backup);
free (backup);
- backup = NULL;
}
+ unlink (tempname);
}
}
-
- if (backup)
+ else
{
- unlink (backup);
- free (backup);
+ /* Mail spool not writable. Copy the tempstr back to mailbox. */
+ rc = mboxrd_copyback (trk, tempstr);
}
}
- unlink (tempname);
free (tempname);
free (hints.tmpdir);
+ mu_stream_unref (tempstr);
}
dmp->uidvalidity_changed = 0;
diff --git a/libproto/mbox/tests/Makefile.am b/libproto/mbox/tests/Makefile.am
index 6aed32968..67a30fe7b 100644
--- a/libproto/mbox/tests/Makefile.am
+++ b/libproto/mbox/tests/Makefile.am
@@ -44,6 +44,7 @@ TESTSUITE_AT += \
notify.at\
header.at\
qget.at\
+ rospool.at\
uid.at\
uidnext.at\
uidvalidity.at
diff --git a/libproto/mbox/tests/rospool.at b/libproto/mbox/tests/rospool.at
new file mode 100644
index 000000000..b1cdb9a24
--- /dev/null
+++ b/libproto/mbox/tests/rospool.at
@@ -0,0 +1,370 @@
+# GNU Mailutils -- a suite of utilities for electronic mail -*- autotest -*-
+# Copyright (C) 2020-2021 Free Software Foundation, Inc.
+#
+# This library is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
+
+AT_SETUP([read-only spool directory])
+
+# Basically, this testcase is a copy of delete.at that is run over mailboxes
+# located in a directory
+not writable to the current user.
+#
+# Obviously, it is useless when run with root privileges.
+
+AT_CHECK([
+mkdir mailspool
+AT_DATA([mailspool/inbox],
+[From hare@wonder.land Mon Jul 29 22:00:08 2002
+Received: (from hare@wonder.land)
+ by wonder.land id 3301
+ for alice@wonder.land; Mon, 29 Jul 2002 22:00:06 +0100
+Date: Mon, 29 Jul 2002 22:00:01 +0100
+From: March Hare <hare@wonder.land>
+Message-Id: <200207292200.3301@wonder.land>
+To: Alice <alice@wonder.land>
+Subject: Invitation
+X-IMAPbase: 10 9
+X-UID: 1
+
+Have some wine
+
+From alice@wonder.land Mon Jul 29 22:00:09 2002
+Received: (from alice@wonder.land)
+ by wonder.land id 3302
+ for hare@wonder.land; Mon, 29 Jul 2002 22:00:07 +0100
+Date: Mon, 29 Jul 2002 22:00:02 +0100
+From: Alice <alice@wonder.land>
+Message-Id: <200207292200.3302@wonder.land>
+To: March Hare <hare@wonder.land>
+Subject: Re: Invitation
+X-UID: 2
+
+I don't see any wine
+
+From hare@wonder.land Mon Jul 29 22:00:10 2002
+Received: (from hare@wonder.land)
+ by wonder.land id 3303
+ for alice@wonder.land; Mon, 29 Jul 2002 22:00:08 +0100
+Date: Mon, 29 Jul 2002 22:00:03 +0100
+From: March Hare <hare@wonder.land>
+Message-Id: <200207292200.3303@wonder.land>
+To: Alice <alice@wonder.land>
+Subject: Re: Invitation
+X-UID: 3
+
+There isn't any
+
+From alice@wonder.land Mon Jul 29 22:00:11 2002
+Received: (from alice@wonder.land)
+ by wonder.land id 3304
+ for hare@wonder.land; Mon, 29 Jul 2002 22:00:09 +0100
+Date: Mon, 29 Jul 2002 22:00:04 +0100
+From: Alice <alice@wonder.land>
+Message-Id: <200207292200.3304@wonder.land>
+To: March Hare <hare@wonder.land>
+Subject: Re: Invitation
+X-UID: 4
+
+Then it wasn't very civil of you to offer it
+
+From hare@wonder.land Mon Jul 29 22:00:12 2002
+Received: (from hare@wonder.land)
+ by wonder.land id 3305
+ for alice@wonder.land; Mon, 29 Jul 2002 22:00:10 +0100
+Date: Mon, 29 Jul 2002 22:00:05 +0100
+From: March Hare <hare@wonder.land>
+Message-Id: <200207292200.3305@wonder.land>
+To: Alice <alice@wonder.land>
+Subject: Re: Invitation
+X-UID: 5
+
+It wasn't very civil of you to sit down without being invited
+
+From alice@wonder.land Mon Jul 29 22:00:13 2002
+Received: (from alice@wonder.land)
+ by wonder.land id 3306
+ for hare@wonder.land; Mon, 29 Jul 2002 22:00:11 +0100
+Date: Mon, 29 Jul 2002 22:00:06 +0100
+From: Alice <alice@wonder.land>
+Message-Id: <200207292200.3306@wonder.land>
+To: March Hare <hare@wonder.land>
+Subject: Re: Invitation
+X-UID: 6
+
+I didn't know it was YOUR table, it's laid for a
+great many more than three.
+
+])
+cp mailspool/inbox mailspool/inbox1
+cp mailspool/inbox mailspool/inbox2
+chmod -w mailspool
+])
+
+AT_DATA([commands],
+[3
+set_deleted
+expunge
+count
+# Message 4 becomes 3 after expunge. Re-select it.
+3
+uid
+headers
+])
+
+AT_CHECK([mbop -m mailspool/inbox < commands],
+[0],
+[3 current message
+3 set_deleted: OK
+expunge: OK
+count: 5
+3 current message
+3 uid: 4
+3 headers: Received:(from alice@wonder.land) by wonder.land id 3304 for hare@wonder.land; Mon, 29 Jul 2002 22:00:09 +0100
+Date:Mon, 29 Jul 2002 22:00:04 +0100
+From:Alice <alice@wonder.land>
+Message-Id:<200207292200.3304@wonder.land>
+To:March Hare <hare@wonder.land>
+Subject:Re: Invitation
+X-UID:4
+
+])
+
+AT_DATA([commands1],
+[1
+set_deleted
+2
+set_deleted
+5
+set_deleted
+expunge
+count
+1
+uid
+headers
+2
+uid
+headers
+3
+uid
+headers
+])
+
+AT_CHECK([mbop -m mailspool/inbox1 < commands1|x_imapbase_normalize],
+[0],
+[1 current message
+1 set_deleted: OK
+2 current message
+2 set_deleted: OK
+5 current message
+5 set_deleted: OK
+expunge: OK
+count: 3
+1 current message
+1 uid: 3
+1 headers: Received:(from hare@wonder.land) by wonder.land id 3303 for alice@wonder.land; Mon, 29 Jul 2002 22:00:08 +0100
+Date:Mon, 29 Jul 2002 22:00:03 +0100
+From:March Hare <hare@wonder.land>
+Message-Id:<200207292200.3303@wonder.land>
+To:Alice <alice@wonder.land>
+Subject:Re: Invitation
+X-IMAPbase:10 9
+X-UID:3
+
+2 current message
+2 uid: 4
+2 headers: Received:(from alice@wonder.land) by wonder.land id 3304 for hare@wonder.land; Mon, 29 Jul 2002 22:00:09 +0100
+Date:Mon, 29 Jul 2002 22:00:04 +0100
+From:Alice <alice@wonder.land>
+Message-Id:<200207292200.3304@wonder.land>
+To:March Hare <hare@wonder.land>
+Subject:Re: Invitation
+X-UID:4
+
+3 current message
+3 uid: 6
+3 headers: Received:(from alice@wonder.land) by wonder.land id 3306 for hare@wonder.land; Mon, 29 Jul 2002 22:00:11 +0100
+Date:Mon, 29 Jul 2002 22:00:06 +0100
+From:Alice <alice@wonder.land>
+Message-Id:<200207292200.3306@wonder.land>
+To:March Hare <hare@wonder.land>
+Subject:Re: Invitation
+X-UID:6
+
+])
+
+# ##
+AT_DATA([commands1a],
+[count
+1
+uid
+headers
+2
+uid
+headers
+3
+uid
+headers
+])
+
+AT_CHECK([mbop -m mailspool/inbox1 < commands1a|x_imapbase_normalize],
+[0],
+[count: 3
+1 current message
+1 uid: 3
+1 headers: Received:(from hare@wonder.land) by wonder.land id 3303 for alice@wonder.land; Mon, 29 Jul 2002 22:00:08 +0100
+Date:Mon, 29 Jul 2002 22:00:03 +0100
+From:March Hare <hare@wonder.land>
+Message-Id:<200207292200.3303@wonder.land>
+To:Alice <alice@wonder.land>
+Subject:Re: Invitation
+X-IMAPbase:10 9
+X-UID:3
+
+2 current message
+2 uid: 4
+2 headers: Received:(from alice@wonder.land) by wonder.land id 3304 for hare@wonder.land; Mon, 29 Jul 2002 22:00:09 +0100
+Date:Mon, 29 Jul 2002 22:00:04 +0100
+From:Alice <alice@wonder.land>
+Message-Id:<200207292200.3304@wonder.land>
+To:March Hare <hare@wonder.land>
+Subject:Re: Invitation
+X-UID:4
+
+3 current message
+3 uid: 6
+3 headers: Received:(from alice@wonder.land) by wonder.land id 3306 for hare@wonder.land; Mon, 29 Jul 2002 22:00:11 +0100
+Date:Mon, 29 Jul 2002 22:00:06 +0100
+From:Alice <alice@wonder.land>
+Message-Id:<200207292200.3306@wonder.land>
+To:March Hare <hare@wonder.land>
+Subject:Re: Invitation
+X-UID:6
+
+])
+
+# ##
+AT_DATA([commands2],
+[1
+set_deleted
+3
+set_deleted
+5
+set_deleted
+expunge
+count
+1
+uid
+headers
+2
+uid
+headers
+3
+uid
+headers
+])
+
+AT_CHECK([mbop -m mailspool/inbox2 < commands2|x_imapbase_normalize],
+[0],
+[1 current message
+1 set_deleted: OK
+3 current message
+3 set_deleted: OK
+5 current message
+5 set_deleted: OK
+expunge: OK
+count: 3
+1 current message
+1 uid: 2
+1 headers: Received:(from alice@wonder.land) by wonder.land id 3302 for hare@wonder.land; Mon, 29 Jul 2002 22:00:07 +0100
+Date:Mon, 29 Jul 2002 22:00:02 +0100
+From:Alice <alice@wonder.land>
+Message-Id:<200207292200.3302@wonder.land>
+To:March Hare <hare@wonder.land>
+Subject:Re: Invitation
+X-IMAPbase:10 9
+X-UID:2
+
+2 current message
+2 uid: 4
+2 headers: Received:(from alice@wonder.land) by wonder.land id 3304 for hare@wonder.land; Mon, 29 Jul 2002 22:00:09 +0100
+Date:Mon, 29 Jul 2002 22:00:04 +0100
+From:Alice <alice@wonder.land>
+Message-Id:<200207292200.3304@wonder.land>
+To:March Hare <hare@wonder.land>
+Subject:Re: Invitation
+X-UID:4
+
+3 current message
+3 uid: 6
+3 headers: Received:(from alice@wonder.land) by wonder.land id 3306 for hare@wonder.land; Mon, 29 Jul 2002 22:00:11 +0100
+Date:Mon, 29 Jul 2002 22:00:06 +0100
+From:Alice <alice@wonder.land>
+Message-Id:<200207292200.3306@wonder.land>
+To:March Hare <hare@wonder.land>
+Subject:Re: Invitation
+X-UID:6
+
+])
+
+AT_DATA([commands2a],
+[count
+1
+uid
+headers
+2
+uid
+headers
+3
+uid
+headers
+])
+
+AT_CHECK([mbop -m mailspool/inbox2 < commands2a|x_imapbase_normalize],
+[0],
+[count: 3
+1 current message
+1 uid: 2
+1 headers: Received:(from alice@wonder.land) by wonder.land id 3302 for hare@wonder.land; Mon, 29 Jul 2002 22:00:07 +0100
+Date:Mon, 29 Jul 2002 22:00:02 +0100
+From:Alice <alice@wonder.land>
+Message-Id:<200207292200.3302@wonder.land>
+To:March Hare <hare@wonder.land>
+Subject:Re: Invitation
+X-IMAPbase:10 9
+X-UID:2
+
+2 current message
+2 uid: 4
+2 headers: Received:(from alice@wonder.land) by wonder.land id 3304 for hare@wonder.land; Mon, 29 Jul 2002 22:00:09 +0100
+Date:Mon, 29 Jul 2002 22:00:04 +0100
+From:Alice <alice@wonder.land>
+Message-Id:<200207292200.3304@wonder.land>
+To:March Hare <hare@wonder.land>
+Subject:Re: Invitation
+X-UID:4
+
+3 current message
+3 uid: 6
+3 headers: Received:(from alice@wonder.land) by wonder.land id 3306 for hare@wonder.land; Mon, 29 Jul 2002 22:00:11 +0100
+Date:Mon, 29 Jul 2002 22:00:06 +0100
+From:Alice <alice@wonder.land>
+Message-Id:<200207292200.3306@wonder.land>
+To:March Hare <hare@wonder.land>
+Subject:Re: Invitation
+X-UID:6
+
+])
+
+AT_CLEANUP
+
diff --git a/libproto/mbox/tests/testsuite.at b/libproto/mbox/tests/testsuite.at
index 223627d6e..3237db286 100644
--- a/libproto/mbox/tests/testsuite.at
+++ b/libproto/mbox/tests/testsuite.at
@@ -34,4 +34,5 @@ m4_include([uidnext.at])
m4_include([notify.at])
+m4_include([rospool.at])

Return to:

Send suggestions and report system problems to the System administrator.