Use safe methods to open the archive mbox and an existing mbox file
This commit is contained in:
parent
f031573071
commit
40f9a4c049
|
@ -331,13 +331,12 @@ class Mbox(mailbox.UnixMailbox):
|
||||||
"""
|
"""
|
||||||
assert(path)
|
assert(path)
|
||||||
self._locked = False
|
self._locked = False
|
||||||
try:
|
fd = safe_open_existing(path)
|
||||||
self.original_atime = os.path.getatime(path)
|
st = os.fstat(fd)
|
||||||
self.original_mtime = os.path.getmtime(path)
|
self.original_atime = st.st_atime
|
||||||
self.starting_size = os.path.getsize(path)
|
self.original_mtime = st.st_mtime
|
||||||
self.mbox_file = open(path, "r+")
|
self.starting_size = st.st_size
|
||||||
except IOError, msg:
|
self.mbox_file = os.fdopen(fd, "r+")
|
||||||
unexpected_error(msg)
|
|
||||||
self.mbox_file_name = path
|
self.mbox_file_name = path
|
||||||
mailbox.UnixMailbox.__init__(self, self.mbox_file)
|
mailbox.UnixMailbox.__init__(self, self.mbox_file)
|
||||||
|
|
||||||
|
@ -604,7 +603,8 @@ class ArchiveMbox(TempMbox):
|
||||||
if not options.no_compress:
|
if not options.no_compress:
|
||||||
final_name = final_name + ".gz"
|
final_name = final_name + ".gz"
|
||||||
vprint("writing back '%s' to '%s'" % (self.mbox_file_name, final_name))
|
vprint("writing back '%s' to '%s'" % (self.mbox_file_name, final_name))
|
||||||
final_archive = open(final_name, "a")
|
fd = safe_open(final_name)
|
||||||
|
final_archive = os.fdopen(fd, "a")
|
||||||
shutil.copyfileobj(self.mbox_file, final_archive)
|
shutil.copyfileobj(self.mbox_file, final_archive)
|
||||||
final_archive.close()
|
final_archive.close()
|
||||||
self.remove()
|
self.remove()
|
||||||
|
@ -1664,6 +1664,55 @@ def get_filename(msg):
|
||||||
return msg.fp._file.name
|
return msg.fp._file.name
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
def safe_open_create(filename):
|
||||||
|
"""Create and open a file in a NFSv2-safe way, and return a r/w file descriptor.
|
||||||
|
The new file is created with mode 600."""
|
||||||
|
# This is essentially a simplified version of the dotlocking function.
|
||||||
|
vprint("Creating file '%s'" % filename)
|
||||||
|
dir, basename = os.path.split(filename)
|
||||||
|
# We rely on tempfile.mkstemp to create files safely and with 600 mode.
|
||||||
|
fd, pre_name = tempfile.mkstemp(prefix=basename+".pre-", dir=dir)
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
os.link(pre_name, filename)
|
||||||
|
except OSError, e:
|
||||||
|
if os.fstat(fd).st_nlink == 2:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
os.unlink(pre_name)
|
||||||
|
return fd
|
||||||
|
|
||||||
|
def safe_open_existing(filename):
|
||||||
|
"""Safely open an existing file, and return a r/w file descriptor."""
|
||||||
|
lst = os.lstat(filename)
|
||||||
|
if stat.S_ISLNK(lst.st_mode):
|
||||||
|
unexpected_error("file '%s' is a symlink." % filename)
|
||||||
|
fd = os.open(filename, os.O_RDWR)
|
||||||
|
fst = os.fstat(fd)
|
||||||
|
if fst.st_nlink != 1:
|
||||||
|
unexpected_error("file '%s' has %d hard links." % \
|
||||||
|
(filename, fst.st_nlink))
|
||||||
|
if stat.S_ISDIR(fst.st_mode):
|
||||||
|
unexpected_error("file '%s' is a directory." % filename)
|
||||||
|
for i in stat.ST_DEV, stat.ST_INO, stat.ST_UID, stat.ST_GID, stat.ST_MODE, stat.ST_NLINK:
|
||||||
|
if fst[i] != lst[i]:
|
||||||
|
unexpected_error("file status changed unexpectedly")
|
||||||
|
return fd
|
||||||
|
|
||||||
|
def safe_open(filename):
|
||||||
|
"""Safely open a file, creating it if it doesn't exist, and return a
|
||||||
|
r/w file descriptor."""
|
||||||
|
# This borrows from postfix code.
|
||||||
|
vprint("Opening archive...")
|
||||||
|
try:
|
||||||
|
fd = safe_open_existing(filename)
|
||||||
|
except OSError, e:
|
||||||
|
if e.errno != errno.ENOENT: raise
|
||||||
|
fd = safe_open_create(filename)
|
||||||
|
return fd
|
||||||
|
|
||||||
# this is where it all happens, folks
|
# this is where it all happens, folks
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
Loading…
Reference in New Issue