Ensure correct operation with read-only target dirs and .do file dirs.

Although I expect this is rather rare, some people may want to build in
a read-write subdir of a read-only tree.  Other than some confusing
error reporting, this works fine in redo after the recent changes to
temp file handling, but let's add a test to make sure it stays that
way.  The test found a bug in minimal/do, so let's fix that.

Reported-by: Jeff Stearns <jeff.stearns@gmail.com>
This commit is contained in:
Avery Pennarun 2018-12-13 12:58:56 +00:00
commit 39e017869d
5 changed files with 44 additions and 16 deletions

View file

@ -374,23 +374,22 @@ class _BuildJob(object):
# FIXME: race condition here between updating stamp/is_generated
# and actually renaming the files into place. There needs to
# be some kind of two-stage commit, I guess.
if st1.st_size > 0:
if st1.st_size > 0 and not st2:
# script wrote to stdout. Copy its contents to the tmpfile.
unlink(self.tmpname)
try:
newf = open(self.tmpname, 'w')
except OSError, e:
dnt = os.path.dirname(t)
except IOError, e:
dnt = os.path.dirname(os.path.abspath(t))
if not os.path.exists(dnt):
# This could happen, so report a simple error message
# that gives a hint for how to fix your .do script.
err('%s: target dir %r does not exist!\n' % (t, dnt))
else:
# I don't know why this would happen, so raise the
# full exception if it ever does.
err('%s: save stdout to %s: %s\n'
% (t, self.tmpname, e))
raise
# This could happen for, eg. a permissions error on
# the target directory.
err('%s: copy stdout: %s\n' % (t, e))
rv = 209
else:
self.outfile.seek(0)
while 1:
@ -407,12 +406,10 @@ class _BuildJob(object):
# Atomically replace the target file
os.rename(self.tmpname, t)
except OSError, e:
# Nowadays self.tmpname is in the same directory as
# t, so there's no very good reason for this to
# fail. Thus, raise the full exception if it ever
# does.
# This could happen for, eg. a permissions error on
# the target directory.
err('%s: rename %s: %s\n' % (t, self.tmpname, e))
raise
rv = 209
else: # no output generated at all; that's ok
unlink(t)
sf = self.sf