;;; Ops that create objects in the file system: ;;; create-{directory,fifo,hard-link,symlink} ;;; Copyright (c) 1993 by Olin Shivers. See file COPYING. ;;; This procedure nukes FNAME, whatever it may be: directory, file, fifo, ;;; symlink. ;;; ;;; We can't probe FNAME to find out what it is and then do the right ;;; delete operation because there's a window in-between the probe and the ;;; delete where the file system can be altered -- the probe and delete ;;; aren't atomic. In order to deliver on our contract, we have to spin ;;; in a funny loop until we win. In practice, the loop will probably never ;;; execute more than once. (define (delete-filesys-object fname) (let loop () (or (with-errno-handler ; Assume it's a file and try. ((err data) ((errno/perm) #f) ; Return #f if directory ((errno/noent) #t)) (delete-file fname) #t) (with-errno-handler ; Assume it's a directory and try. ((err data) ((errno/notdir) #f) ; Return #f if fname is not a directory. ((errno/noent) #t)) (delete-directory fname) #t) (loop)))) ; Strange things are happening. Try again. ;;; For similar reasons, all of these ops must loop. ;;; Abstract out common code for create-{directory,fifo,hard-link,symlink}: (define (create-file-thing fname makeit override? op-name syscall) (let ((query (lambda () (y-or-n? (string-append op-name ": " fname " already exists. Delete"))))) (let loop ((override? override?)) ;; MAKEIT returns #f if win, errno if lose. (cond ((makeit fname) => (lambda (err) (if (not (= err errno/exist)) (errno-error err syscall fname) ;; FNAME exists. Nuke it and retry? (cond ((if (eq? override? 'query) (query) override?) (delete-filesys-object fname) (loop #t)) (else (errno-error err syscall fname)))))))))) ;;;;;;; (define (create-directory dir . rest) (let ((perms (if (null? rest) #o777 (car rest))) (override? (if (or (null? rest) (null? (cdr rest))) #f (cadr rest)))) (create-file-thing dir (lambda (dir) (create-directory/errno dir perms)) override? "create-directory" create-directory))) (define (create-fifo fifo . rest) (let ((perms (if (null? rest) #o777 (car rest))) (override? (if (or (null? rest) (null? (cdr rest))) #f (cadr rest)))) (create-file-thing fifo (lambda (fifo) (create-fifo/errno fifo perms)) override? "create-fifo" create-fifo))) (define (create-hard-link old-fname new-fname . maybe-override?) (create-file-thing new-fname (lambda (new-fname) (create-hard-link/errno old-fname new-fname)) (:optional maybe-override? #f) "create-hard-link" create-hard-link)) (define (create-symlink old-fname new-fname . maybe-override?) (create-file-thing new-fname (lambda (symlink) (create-symlink/errno old-fname symlink)) (:optional maybe-override? #f) "create-symlink" create-symlink)) ;;; Unix rename() works backwards from mkdir(), mkfifo(), link(), and ;;; symlink() -- it overrides by default, (though insisting on a type ;;; match between the old and new object). So we can't use create-file-thing. ;;; Note that this loop has a tiny atomicity problem -- if someone ;;; creates a file *after* we do our existence check, but *before* we ;;; do the rename, we could end up overriding it, when the user asked ;;; us not to. That's life in the food chain. (define (rename-file old-fname new-fname . maybe-override?) (let ((override? (:optional maybe-override? #f))) (if (or (and override? (not (eq? override? 'query))) (file-not-exists? new-fname) (and override? (y-or-n? (string-append "rename-file:" new-fname " already exists. Delete")))) (%rename-file old-fname new-fname))))