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:
parent
d95277d121
commit
39e017869d
5 changed files with 44 additions and 16 deletions
|
|
@ -36,7 +36,7 @@ _dirsplit()
|
|||
}
|
||||
|
||||
# Like /usr/bin/dirname, but avoids a fork and uses _dirsplit semantics.
|
||||
dirname()
|
||||
qdirname()
|
||||
(
|
||||
_dirsplit "$1"
|
||||
dir=${_dirsplit_dir%/}
|
||||
|
|
@ -283,7 +283,7 @@ _run_dofile()
|
|||
# done.
|
||||
_do()
|
||||
{
|
||||
local dir="$1" target="$1$2" tmp="$1$2.redo.tmp"
|
||||
local dir="$1" target="$1$2" tmp="$1$2.redo.tmp" tdir=
|
||||
local dopath= dodir= dofile= ext=
|
||||
if [ "$_cmd" = "redo" ] ||
|
||||
( [ ! -e "$target" -o -d "$target" ] &&
|
||||
|
|
@ -309,7 +309,8 @@ _do()
|
|||
target=$(_relpath "$target" "$PWD") || return 98
|
||||
tmp=$(_relpath "$tmp" "$PWD") || return 97
|
||||
base=${target%$ext}
|
||||
[ ! -e "$DO_BUILT" ] || [ ! -d "$(dirname "$target")" ] ||
|
||||
tdir=$(qdirname "$target")
|
||||
[ ! -e "$DO_BUILT" ] || [ ! -w "$tdir/." ] ||
|
||||
: >>"$target.did.tmp"
|
||||
# $qtmp is a temporary file used to capture stdout.
|
||||
# Since it might be accidentally deleted as a .do 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
|
||||
|
|
|
|||
1
t/204-readonly/.gitignore
vendored
Normal file
1
t/204-readonly/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/rodir
|
||||
25
t/204-readonly/all.do
Normal file
25
t/204-readonly/all.do
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
[ -e rodir ] && chmod u+w rodir
|
||||
[ -e rodir/rwdir ] && chmod u+w rodir/rwdir
|
||||
rm -rf rodir
|
||||
mkdir rodir rodir/rwdir
|
||||
|
||||
cd rodir
|
||||
cat >default.ro1.do <<-EOF
|
||||
chmod u+w "\$(dirname "\$1")"
|
||||
echo 'redir' >\$3
|
||||
EOF
|
||||
cat >default.ro2.do <<-EOF
|
||||
chmod u+w "\$(dirname "\$1")"
|
||||
echo 'stdout'
|
||||
EOF
|
||||
|
||||
# Check that:
|
||||
# - redo works when the .do file is in a read-only directory.
|
||||
# - redo works when the target is in a read-only directory that becomes
|
||||
# writable only *after* launching the .do script. (For example, the .do
|
||||
# might mount a new read-write filesystem in an otherwise read-only
|
||||
# tree.)
|
||||
chmod a-w . rwdir
|
||||
redo rwdir/a.ro1
|
||||
chmod a-w . rwdir
|
||||
redo rwdir/a.ro2
|
||||
4
t/204-readonly/clean.do
Normal file
4
t/204-readonly/clean.do
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
[ -e rodir ] && chmod u+w rodir
|
||||
[ -e rodir/rwdir ] && chmod u+w rodir/rwdir
|
||||
rm -rf rodir
|
||||
rm -f *~ .*~
|
||||
Loading…
Add table
Add a link
Reference in a new issue