summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Wingo <wingo@pobox.com>2011-03-29 11:40:05 +0200
committerAndy Wingo <wingo@pobox.com>2011-03-29 11:40:05 +0200
commit56dbc8a89958fcc401b0980ffcd0047f20470cd3 (patch)
tree855e3169db5474ce0f18b90819f1629362f6e4fd
parent9dadfa47b07548ff5cf3604067910c8aece93c42 (diff)
downloadguile-56dbc8a89958fcc401b0980ffcd0047f20470cd3.tar.gz
rewrite ensure-writable-dir to not be racy
* module/system/base/compile.scm (ensure-writable-dir): Rewrite to not be racy.
-rw-r--r--module/system/base/compile.scm29
1 files changed, 20 insertions, 9 deletions
diff --git a/module/system/base/compile.scm b/module/system/base/compile.scm
index 7d46713b2..1b6e73f32 100644
--- a/module/system/base/compile.scm
+++ b/module/system/base/compile.scm
@@ -68,16 +68,27 @@
x
(lookup-language x)))
-;; Throws an exception if `dir' is not writable. The double-stat is OK,
-;; as this is only used during compilation.
+;; Throws an exception if `dir' is not writable. The mkdir occurs
+;; before the check, so that we avoid races (possibly due to parallel
+;; compilation).
+;;
(define (ensure-writable-dir dir)
- (if (file-exists? dir)
- (if (access? dir W_OK)
- #t
- (error "directory not writable" dir))
- (begin
- (ensure-writable-dir (dirname dir))
- (mkdir dir))))
+ (catch 'system-error
+ (lambda ()
+ (mkdir dir))
+ (lambda (k subr fmt args rest)
+ (let ((errno (and (pair? rest) (car rest))))
+ (cond
+ ((eqv? errno EEXIST)
+ (let ((st (stat dir)))
+ (if (or (not (eq? (stat:type st) 'directory))
+ (not (access? dir W_OK)))
+ (error "directory not writable" dir))))
+ ((eqv? errno ENOENT)
+ (ensure-writable-dir (dirname dir))
+ (ensure-writable-dir dir))
+ (else
+ (throw k subr fmt args rest)))))))
;;; This function is among the trickiest I've ever written. I tried many
;;; variants. In the end, simple is best, of course.