mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-29 19:30:36 +02:00
Accidentally omitted from b3b7477128
.
* test-suite/tests/filesys.test: skip copy-file EACCES test when root.
844 lines
29 KiB
Scheme
844 lines
29 KiB
Scheme
;;;; filesys.test --- test file system functions -*- scheme -*-
|
|
;;;;
|
|
;;;; Copyright (C) 2004, 2006, 2013, 2019, 2021 Free Software Foundation, Inc.
|
|
;;;; Copyright (C) 2021 Maxime Devos <maximedevos@telenet.be>
|
|
;;;;
|
|
;;;; This library is free software; you can redistribute it and/or
|
|
;;;; modify it under the terms of the GNU Lesser General Public
|
|
;;;; License as published by the Free Software Foundation; either
|
|
;;;; version 3 of the License, or (at your option) any later version.
|
|
;;;;
|
|
;;;; This library is distributed in the hope that it will be useful,
|
|
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
;;;; Lesser General Public License for more details.
|
|
;;;;
|
|
;;;; You should have received a copy of the GNU Lesser General Public
|
|
;;;; License along with this library; if not, write to the Free Software
|
|
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
(define-module (test-suite test-filesys)
|
|
#:use-module (test-suite lib)
|
|
#:use-module (test-suite guile-test)
|
|
#:use-module (ice-9 threads)
|
|
#:use-module (ice-9 match)
|
|
#:use-module (rnrs io ports)
|
|
#:use-module (rnrs bytevectors))
|
|
|
|
(define (test-file)
|
|
(data-file-name "filesys-test.tmp"))
|
|
(define (test-symlink)
|
|
(data-file-name "filesys-test-link.tmp"))
|
|
(define (test-directory)
|
|
(data-file-name "filesys-test-dir.tmp"))
|
|
(define (test-directory2)
|
|
(data-file-name "filesys-test-dir2.tmp"))
|
|
|
|
(define (skip-on-darwin)
|
|
(when (string-ci=? "darwin" (utsname:sysname (uname)))
|
|
(throw 'untested)))
|
|
|
|
;;;
|
|
;;; copy-file
|
|
;;;
|
|
|
|
(with-test-prefix "copy-file"
|
|
|
|
(with-test-prefix "missing source"
|
|
(with-exception-handler
|
|
(λ (ex) (pass-if-equal "errno is ENOENT" ENOENT (exception-errno ex)))
|
|
(λ () (copy-file "this-file-should-not-exist" "somewhere"))
|
|
#:unwind? #t))
|
|
|
|
(call-with-temp-dir
|
|
"copy-file-XXXXXX"
|
|
(λ (tmpdir)
|
|
(let ((msg "this is a test, this is only a test...\n")
|
|
(src (string-append tmpdir "/" "source"))
|
|
(dest (string-append tmpdir "/" "read-only-dest")))
|
|
(call-with-output-file src (λ (p) (display msg p)))
|
|
(call-with-output-file dest identity)
|
|
(with-test-prefix "successful copy"
|
|
(copy-file src dest)
|
|
(pass-if-equal "copy-file dest content" msg
|
|
(call-with-input-file dest get-string-all)))
|
|
(unless (zero? (geteuid))
|
|
(with-test-prefix "read only dest"
|
|
(chmod dest #o444)
|
|
(with-exception-handler
|
|
(λ (ex)
|
|
(pass-if-equal "errno is EACCES" EACCES (exception-errno ex)))
|
|
(λ () (copy-file src dest))
|
|
#:unwind? #t))))))
|
|
|
|
;; return next prospective file descriptor number
|
|
(define (next-fd)
|
|
(let ((fd (dup 0)))
|
|
(close fd)
|
|
fd))
|
|
|
|
;; in guile 1.6.4 and earlier, copy-file didn't close the input fd when
|
|
;; the output could not be opened
|
|
(pass-if "fd leak when dest unwritable"
|
|
(let ((old-next (next-fd)))
|
|
(false-if-exception (copy-file "/dev/null" "no/such/dir/foo"))
|
|
(= old-next (next-fd)))))
|
|
|
|
;;;
|
|
;;; lstat
|
|
;;;
|
|
|
|
(with-test-prefix "lstat"
|
|
|
|
(pass-if "normal file"
|
|
(call-with-output-file (test-file)
|
|
(lambda (port)
|
|
(display "hello" port)))
|
|
(eqv? 5 (stat:size (lstat (test-file)))))
|
|
|
|
(call-with-output-file (test-file)
|
|
(lambda (port)
|
|
(display "hello" port)))
|
|
(false-if-exception (delete-file (test-symlink)))
|
|
(if (not (false-if-exception
|
|
(begin (symlink (test-file) (test-symlink)) #t)))
|
|
(display "cannot create symlink, lstat test skipped\n")
|
|
(pass-if "symlink"
|
|
;; not much to test, except that it works
|
|
(->bool (lstat (test-symlink))))))
|
|
|
|
;;;
|
|
;;; opendir and friends
|
|
;;;
|
|
|
|
(with-test-prefix "opendir"
|
|
|
|
(with-test-prefix "root directory"
|
|
(let ((d (opendir "/")))
|
|
(pass-if "not empty"
|
|
(string? (readdir d)))
|
|
(pass-if "all entries are strings"
|
|
(let more ()
|
|
(let ((f (readdir d)))
|
|
(cond ((string? f)
|
|
(more))
|
|
((eof-object? f)
|
|
#t)
|
|
(else
|
|
#f)))))
|
|
(closedir d))))
|
|
|
|
;;;
|
|
;;; stat
|
|
;;;
|
|
|
|
(with-test-prefix "stat"
|
|
|
|
(with-test-prefix "filename"
|
|
|
|
(pass-if "size"
|
|
(call-with-output-file (test-file)
|
|
(lambda (port)
|
|
(display "hello" port)))
|
|
(eqv? 5 (stat:size (stat (test-file))))))
|
|
|
|
(with-test-prefix "file descriptor"
|
|
|
|
(pass-if "size"
|
|
(call-with-output-file (test-file)
|
|
(lambda (port)
|
|
(display "hello" port)))
|
|
(let* ((fd (open-fdes (test-file) O_RDONLY))
|
|
(st (stat fd)))
|
|
(close-fdes fd)
|
|
(eqv? 5 (stat:size st)))))
|
|
|
|
(with-test-prefix "port"
|
|
|
|
(pass-if "size"
|
|
(call-with-output-file (test-file)
|
|
(lambda (port)
|
|
(display "hello" port)))
|
|
(let* ((port (open-file (test-file) "r+"))
|
|
(st (stat port)))
|
|
(close-port port)
|
|
(eqv? 5 (stat:size st))))))
|
|
|
|
(with-test-prefix "statat"
|
|
;; file-exists? from (ice-9 boot) dereferences symbolic links
|
|
;; (a bug?).
|
|
(define (file-exists? filename)
|
|
(catch 'system-error
|
|
(lambda () (lstat filename) #t)
|
|
(lambda args
|
|
(if (= (system-error-errno args) ENOENT)
|
|
;; For the purposes of the following tests,
|
|
;; it is safe to ignore errors like EPERM, but a correct
|
|
;; implementation would return #t for that error.
|
|
#f
|
|
(apply throw args)))))
|
|
(define (maybe-delete-directory)
|
|
(when (file-exists? (test-directory))
|
|
(for-each
|
|
(lambda (filename)
|
|
(define full-name (in-vicinity (test-directory) filename))
|
|
(when (file-exists? full-name)
|
|
(delete-file full-name)))
|
|
'("test-file" "test-symlink"))
|
|
(rmdir (test-directory))))
|
|
(define (skip-unless-defined . things)
|
|
(for-each (lambda (thing)
|
|
(unless (defined? thing)
|
|
(throw 'unsupported)))
|
|
things))
|
|
(maybe-delete-directory)
|
|
(mkdir (test-directory))
|
|
(call-with-output-file (in-vicinity (test-directory) "test-file")
|
|
(lambda (port)
|
|
(display "hello" port)))
|
|
|
|
;; Return #true if the symlink was created, #false otherwise.
|
|
(define (maybe-create-symlink)
|
|
(if (file-exists? (in-vicinity (test-directory) "test-symlink"))
|
|
#t
|
|
(false-if-exception
|
|
(symlink "test-file"
|
|
(in-vicinity (test-directory) "test-symlink")))))
|
|
|
|
(pass-if-equal "regular file" 5
|
|
(skip-unless-defined 'statat)
|
|
(call-with-port
|
|
(open (test-directory) O_RDONLY)
|
|
(lambda (port)
|
|
(stat:size (statat port "test-file")))))
|
|
|
|
(pass-if-equal "regular file, AT_SYMLINK_NOFOLLOW" 5
|
|
(skip-unless-defined 'statat 'AT_SYMLINK_NOFOLLOW)
|
|
(call-with-port
|
|
(open (test-directory) O_RDONLY)
|
|
(lambda (port)
|
|
(stat:size (statat port "test-file" AT_SYMLINK_NOFOLLOW)))))
|
|
|
|
(pass-if-equal "symbolic links are dereferenced" '(regular 5)
|
|
;; Not all systems support symlinks.
|
|
(skip-unless-defined 'statat 'symlink)
|
|
(unless (maybe-create-symlink)
|
|
(throw 'unresolved))
|
|
(call-with-port
|
|
(open (test-directory) O_RDONLY)
|
|
(lambda (port)
|
|
(define result (statat port "test-symlink"))
|
|
(list (stat:type result) (stat:size result)))))
|
|
|
|
(pass-if-equal "symbolic links are not dereferenced"
|
|
`(symlink ,(string-length "test-file"))
|
|
;; Not all systems support symlinks.
|
|
(skip-unless-defined 'statat 'symlink)
|
|
(unless (maybe-create-symlink)
|
|
(throw 'unresolved))
|
|
(call-with-port
|
|
(open (test-directory) O_RDONLY)
|
|
(lambda (port)
|
|
(define result (statat port "test-symlink" AT_SYMLINK_NOFOLLOW))
|
|
(list (stat:type result) (stat:size result)))))
|
|
|
|
(maybe-delete-directory))
|
|
|
|
(with-test-prefix "sendfile"
|
|
|
|
(let* ((file (search-path %load-path "ice-9/boot-9.scm"))
|
|
(len (stat:size (stat file)))
|
|
(ref (call-with-input-file file get-bytevector-all)))
|
|
|
|
(pass-if-equal "file" (cons len ref)
|
|
(let* ((result (call-with-input-file file
|
|
(lambda (input)
|
|
(call-with-output-file (test-file)
|
|
(lambda (output)
|
|
(sendfile output input len 0))))))
|
|
(out (call-with-input-file (test-file) get-bytevector-all)))
|
|
(cons result out)))
|
|
|
|
(pass-if-equal "file with offset"
|
|
(cons (- len 777) (call-with-input-file file
|
|
(lambda (input)
|
|
(seek input 777 SEEK_SET)
|
|
(get-bytevector-all input))))
|
|
(let* ((result (call-with-input-file file
|
|
(lambda (input)
|
|
(call-with-output-file (test-file)
|
|
(lambda (output)
|
|
(sendfile output input (- len 777) 777))))))
|
|
(out (call-with-input-file (test-file) get-bytevector-all)))
|
|
(cons result out)))
|
|
|
|
(pass-if-equal "file with offset past the end"
|
|
(cons (- len 777) (call-with-input-file file
|
|
(lambda (input)
|
|
(seek input 777 SEEK_SET)
|
|
(get-bytevector-all input))))
|
|
(let* ((result (call-with-input-file file
|
|
(lambda (input)
|
|
(call-with-output-file (test-file)
|
|
(lambda (output)
|
|
(sendfile output input len 777))))))
|
|
(out (call-with-input-file (test-file) get-bytevector-all)))
|
|
(cons result out)))
|
|
|
|
(pass-if-equal "file with offset near the end"
|
|
(cons 77 (call-with-input-file file
|
|
(lambda (input)
|
|
(seek input (- len 77) SEEK_SET)
|
|
(get-bytevector-all input))))
|
|
(let* ((result (call-with-input-file file
|
|
(lambda (input)
|
|
(call-with-output-file (test-file)
|
|
(lambda (output)
|
|
(sendfile output input len (- len 77)))))))
|
|
(out (call-with-input-file (test-file) get-bytevector-all)))
|
|
(cons result out)))
|
|
|
|
(pass-if-equal "pipe" (cons len ref)
|
|
(if (provided? 'threads)
|
|
(let* ((in+out (pipe))
|
|
(child (call-with-new-thread
|
|
(lambda ()
|
|
(call-with-input-file file
|
|
(lambda (input)
|
|
(let ((result (sendfile (cdr in+out)
|
|
(fileno input)
|
|
len 0)))
|
|
(close-port (cdr in+out))
|
|
result)))))))
|
|
(let ((out (get-bytevector-all (car in+out))))
|
|
(close-port (car in+out))
|
|
(cons (join-thread child) out)))
|
|
(throw 'unresolved)))
|
|
|
|
(pass-if-equal "pipe with offset"
|
|
(cons (- len 777) (call-with-input-file file
|
|
(lambda (input)
|
|
(seek input 777 SEEK_SET)
|
|
(get-bytevector-all input))))
|
|
(if (provided? 'threads)
|
|
(let* ((in+out (pipe))
|
|
(child (call-with-new-thread
|
|
(lambda ()
|
|
(call-with-input-file file
|
|
(lambda (input)
|
|
(let ((result (sendfile (cdr in+out)
|
|
(fileno input)
|
|
(- len 777)
|
|
777)))
|
|
(close-port (cdr in+out))
|
|
result)))))))
|
|
(let ((out (get-bytevector-all (car in+out))))
|
|
(close-port (car in+out))
|
|
(cons (join-thread child) out)))
|
|
(throw 'unresolved)))))
|
|
|
|
(with-test-prefix "basename"
|
|
|
|
(pass-if-equal "/" "/" (basename "/"))
|
|
(pass-if-equal "//" "/" (basename "//"))
|
|
(pass-if-equal "a/b/c" "c" (basename "a/b/c"))
|
|
|
|
(pass-if-equal "a.b" (basename "a.b" "a.b"))
|
|
(pass-if-equal "a.b" (basename "/a.b" "a.b"))
|
|
(pass-if-equal "a" (basename "a.b" ".b"))
|
|
(pass-if-equal "a" (basename "/a.b" ".b"))
|
|
|
|
;; https://debbugs.gnu.org/69437
|
|
(pass-if-equal "bar" (basename "foo/bar" "o/bar")))
|
|
|
|
(delete-file (test-file))
|
|
(when (file-exists? (test-symlink))
|
|
(delete-file (test-symlink)))
|
|
|
|
|
|
(with-test-prefix "mkdtemp"
|
|
|
|
(pass-if-exception "number arg" exception:wrong-type-arg
|
|
(if (not (defined? 'mkdtemp))
|
|
(throw 'unresolved)
|
|
(mkdtemp 123)))
|
|
|
|
(pass-if "template prefix is preserved"
|
|
(if (not (defined? 'mkdtemp))
|
|
(throw 'unresolved)
|
|
(let* ((template "T-XXXXXX")
|
|
(name (mkdtemp template)))
|
|
(false-if-exception (rmdir name))
|
|
(and
|
|
(string? name)
|
|
(string-contains name "T-")
|
|
(= (string-length name) 8)))))
|
|
|
|
(pass-if-exception "invalid template" exception:system-error
|
|
;; MacOS accepts any template and just does no replacements.
|
|
(skip-on-darwin)
|
|
(if (not (defined? 'mkdtemp))
|
|
(throw 'unresolved)
|
|
(mkdtemp "T-AAAAAA")))
|
|
|
|
(pass-if "directory created"
|
|
(if (not (defined? 'mkdtemp))
|
|
(throw 'unresolved)
|
|
(let* ((template "T-XXXXXX")
|
|
(name (mkdtemp template)))
|
|
(let* ((_stat (stat name))
|
|
(result (eqv? 'directory (stat:type _stat))))
|
|
(false-if-exception (rmdir name))
|
|
result)))))
|
|
|
|
;;;
|
|
;;; chmodat
|
|
;;;
|
|
|
|
(with-test-prefix "chmodat"
|
|
(call-with-output-file (test-file) (const #f))
|
|
(chmod (test-file) #o000)
|
|
|
|
(pass-if-equal "regular file"
|
|
#o300
|
|
(unless (defined? 'chmodat)
|
|
(throw 'unsupported))
|
|
(call-with-port
|
|
(open (dirname (test-file)) O_RDONLY)
|
|
(lambda (port)
|
|
(chmodat port (test-file) #o300)))
|
|
(stat:perms (stat (test-file))))
|
|
|
|
(chmod (test-file) #o000)
|
|
|
|
(pass-if-equal "regular file, AT_SYMLINK_NOFOLLOW"
|
|
#o300
|
|
(unless (and (defined? 'chmodat)
|
|
(defined? 'AT_SYMLINK_NOFOLLOW))
|
|
(throw 'unsupported))
|
|
(call-with-port
|
|
(open (dirname (test-file)) O_RDONLY)
|
|
(lambda (port)
|
|
(catch 'system-error
|
|
(lambda ()
|
|
(chmodat port (basename (test-file)) #o300 AT_SYMLINK_NOFOLLOW))
|
|
(lambda args
|
|
(close-port port)
|
|
;; AT_SYMLINK_NOFOLLOW is not supported on Linux (at least Linux
|
|
;; 5.11.2 with the btrfs file system), even for regular files.
|
|
(if (= ENOTSUP (system-error-errno args))
|
|
(begin
|
|
(display "fchmodat doesn't support AT_SYMLINK_NOFOLLOW\n")
|
|
(throw 'unresolved))
|
|
(apply throw args))))))
|
|
(stat:perms (stat (test-file))))
|
|
|
|
(pass-if-exception "not a port" exception:wrong-type-arg
|
|
(chmodat "bogus" (test-file) #o300))
|
|
|
|
(pass-if-exception "not a file port" exception:wrong-type-arg
|
|
(chmodat (open-input-string "") (test-file) #o300))
|
|
|
|
(pass-if-exception "closed port" exception:wrong-type-arg
|
|
(chmodat (call-with-port (open "." O_RDONLY) identity) (test-file) #o300))
|
|
|
|
(delete-file (test-file)))
|
|
|
|
(with-test-prefix "chdir"
|
|
(pass-if-equal "current directory" (getcwd)
|
|
(begin (chdir ".") (getcwd)))
|
|
(define file (search-path %load-path "ice-9/boot-9.scm"))
|
|
|
|
|
|
;; canonicalize-path is used to not stumble over differences due to links
|
|
(pass-if-equal "test directory" (canonicalize-path (dirname file))
|
|
(let ((olddir (getcwd))
|
|
(dir #f))
|
|
(chdir (canonicalize-path (dirname file)))
|
|
(set! dir (getcwd))
|
|
(chdir olddir)
|
|
dir))
|
|
|
|
(pass-if-equal "test directory, via port" (canonicalize-path (dirname file))
|
|
(unless (provided? 'chdir-port)
|
|
(throw 'unresolved))
|
|
(let ((olddir (getcwd))
|
|
(port (open (canonicalize-path (dirname file)) O_RDONLY))
|
|
(dir #f))
|
|
(chdir port)
|
|
(set! dir (getcwd))
|
|
(chdir olddir)
|
|
dir))
|
|
|
|
(pass-if-exception "closed port" exception:wrong-type-arg
|
|
(unless (provided? 'chdir-port)
|
|
(throw 'unresolved))
|
|
(let ((port (open (dirname file) O_RDONLY))
|
|
(olddir (getcwd)))
|
|
(close-port port)
|
|
(chdir port)
|
|
(chdir olddir))) ; should not be reached
|
|
|
|
(pass-if-exception "not a port or file name" exception:wrong-type-arg
|
|
(chdir '(stuff)))
|
|
|
|
(pass-if-exception "non-file port" exception:wrong-type-arg
|
|
(chdir (open-input-string ""))))
|
|
|
|
(with-test-prefix "readlink"
|
|
(false-if-exception (delete-file (test-symlink)))
|
|
(false-if-exception (delete-file (test-file)))
|
|
(call-with-output-file (test-file)
|
|
(lambda (port)
|
|
(display "hello" port)))
|
|
(if (not (false-if-exception
|
|
(begin (symlink (test-file) (test-symlink)) #t)))
|
|
(display "cannot create symlink, some readlink tests skipped\n")
|
|
(let ()
|
|
(pass-if-equal "file name of symlink" (test-file)
|
|
(readlink (test-symlink)))
|
|
|
|
(pass-if-equal "port representing a symlink" (test-file)
|
|
(let ()
|
|
(unless (and (provided? 'readlink-port)
|
|
(defined? 'O_NOFOLLOW)
|
|
(defined? 'O_PATH)
|
|
(not (= 0 O_NOFOLLOW))
|
|
(not (= 0 O_PATH)))
|
|
(throw 'unsupported))
|
|
(define port (open (test-symlink) (logior O_NOFOLLOW O_PATH)))
|
|
(define points-to (false-if-exception (readlink port)))
|
|
(close-port port)
|
|
points-to))
|
|
|
|
(pass-if-exception "not a port or file name" exception:wrong-type-arg
|
|
(readlink '(stuff)))))
|
|
|
|
(pass-if-equal "port representing a regular file" EINVAL
|
|
(call-with-input-file (test-file)
|
|
(lambda (port)
|
|
(unless (provided? 'readlink-port)
|
|
(throw 'unsupported))
|
|
(catch 'system-error
|
|
(lambda ()
|
|
(readlink port)
|
|
(close-port port) ; should be unreachable
|
|
#f)
|
|
(lambda args
|
|
(close-port port)
|
|
;; At least Linux 5.10.46 returns ENOENT instead of EINVAL.
|
|
;; Possibly surprising, but it is documented in some man
|
|
;; pages and it doesn't appear to be an accident:
|
|
;; <https://elixir.bootlin.com/linux/v5.10.46/source/fs/stat.c#L419>.
|
|
(define error (system-error-errno args))
|
|
(if (= error ENOENT)
|
|
EINVAL
|
|
error))))))
|
|
|
|
(pass-if-exception "non-file port" exception:wrong-type-arg
|
|
(readlink (open-input-string "")))
|
|
|
|
(pass-if-exception "closed port" exception:wrong-type-arg
|
|
(let ((port (open-file (test-file) "r")))
|
|
(close-port port)
|
|
(readlink port)))
|
|
|
|
(false-if-exception (delete-file (test-symlink)))
|
|
(false-if-exception (delete-file (test-file))))
|
|
|
|
(with-test-prefix "symlinkat"
|
|
(pass-if-equal "create" (test-file)
|
|
(unless (defined? 'symlinkat)
|
|
(throw 'unsupported))
|
|
(call-with-port
|
|
(open "." O_RDONLY)
|
|
(lambda (port)
|
|
(symlinkat port (test-file) (test-symlink))
|
|
(readlink (test-symlink)))))
|
|
(false-if-exception (delete-file (test-symlink)))
|
|
|
|
(pass-if-exception "not a port" exception:wrong-type-arg
|
|
(unless (defined? 'symlinkat)
|
|
(throw 'unsupported))
|
|
(symlinkat "bogus" (test-file) (test-symlink)))
|
|
|
|
(pass-if-exception "not a file port" exception:wrong-type-arg
|
|
(unless (defined? 'symlinkat)
|
|
(throw 'unsupported))
|
|
(symlinkat (open-input-string "") (test-file) (test-symlink)))
|
|
|
|
(pass-if-exception "closed port" exception:wrong-type-arg
|
|
(unless (defined? 'symlinkat)
|
|
(throw 'unsupported))
|
|
(symlinkat (call-with-port (open "." O_RDONLY) identity)
|
|
(test-file) (test-symlink))))
|
|
|
|
(with-test-prefix "mkdirat"
|
|
(define (skip-if-unsupported)
|
|
(unless (defined? 'mkdirat)
|
|
(throw 'unsupported)))
|
|
(define (maybe-delete-directory)
|
|
(when (file-exists? (test-directory))
|
|
(rmdir (test-directory))))
|
|
(maybe-delete-directory)
|
|
|
|
(pass-if-equal "create" 'directory
|
|
(skip-if-unsupported)
|
|
(call-with-port
|
|
(open "." O_RDONLY)
|
|
(lambda (port)
|
|
(mkdirat port (test-directory))
|
|
(stat:type (stat (test-directory))))))
|
|
(maybe-delete-directory)
|
|
|
|
(pass-if-equal "explicit perms" (logand #o111 (lognot (umask)))
|
|
(skip-if-unsupported)
|
|
(call-with-port
|
|
(open "." O_RDONLY)
|
|
(lambda (port)
|
|
(mkdirat port (test-directory) #o111)
|
|
(stat:perms (stat (test-directory))))))
|
|
(maybe-delete-directory)
|
|
|
|
(pass-if-equal "create, implicit perms" (logand #o777 (lognot (umask)))
|
|
(skip-if-unsupported)
|
|
(call-with-port
|
|
(open "." O_RDONLY)
|
|
(lambda (port)
|
|
(mkdirat port (test-directory))
|
|
(stat:perms (stat (test-directory))))))
|
|
(maybe-delete-directory))
|
|
|
|
(with-test-prefix "rename-file-at"
|
|
(define (skip-if-unsupported)
|
|
(unless (defined? 'rename-file-at)
|
|
(throw 'unsupported)))
|
|
(pass-if-equal "current working directory" '(#f "hello")
|
|
(skip-if-unsupported)
|
|
;; Create a file in the test directory
|
|
(call-with-output-file "filesys-test-a.tmp"
|
|
(lambda (port) (display "hello" port)))
|
|
;; Try to rename it
|
|
(rename-file-at #f "filesys-test-a.tmp" #f "filesys-test-b.tmp")
|
|
;; Verify it exists under the new name, and not under the old name
|
|
(list (file-exists? "filesys-test-a.tmp")
|
|
(call-with-input-file "filesys-test-b.tmp" get-string-all)))
|
|
|
|
(false-if-exception (delete-file "filesys-test-a.tmp"))
|
|
(false-if-exception (delete-file "filesys-test-b.tmp"))
|
|
|
|
(pass-if-equal "two ports" '(#f "hello")
|
|
(skip-if-unsupported)
|
|
(mkdir (test-directory))
|
|
(mkdir (test-directory2))
|
|
;; Create a file in the first directory
|
|
(call-with-output-file (in-vicinity (test-directory) "a")
|
|
(lambda (port) (display "hello" port)))
|
|
(let ((port1 (open (test-directory) O_RDONLY))
|
|
(port2 (open (test-directory2) O_RDONLY)))
|
|
;; Try to rename it
|
|
(rename-file-at port1 "a" port2 "b")
|
|
(close-port port1)
|
|
(close-port port2)
|
|
;; Verify it exists under the new name, and not under the old name
|
|
(list (file-exists? (in-vicinity (test-directory) "a"))
|
|
(call-with-input-file (in-vicinity (test-directory2) "b")
|
|
get-string-all))))
|
|
(false-if-exception (delete-file (in-vicinity (test-directory) "a")))
|
|
(false-if-exception (delete-file (in-vicinity (test-directory2) "b")))
|
|
(false-if-exception (rmdir (test-directory)))
|
|
(false-if-exception (rmdir (test-directory2)))
|
|
|
|
(pass-if-equal "port and current working directory" '(#f "hello")
|
|
(skip-if-unsupported)
|
|
(mkdir (test-directory))
|
|
;; Create a file in (test-directory)
|
|
(call-with-output-file (in-vicinity (test-directory) "a")
|
|
(lambda (port) (display "hello" port)))
|
|
(let ((port (open (test-directory) O_RDONLY)))
|
|
;; Try to rename it
|
|
(rename-file-at port "a" #f (basename (test-file)))
|
|
(close-port port)
|
|
;; Verify it exists under the new name, and not under the old name.
|
|
(list (file-exists? (in-vicinity (test-directory) "a"))
|
|
(call-with-input-file (test-file) get-string-all))))
|
|
(false-if-exception (delete-file (in-vicinity (test-directory) "a")))
|
|
(false-if-exception (rmdir (test-directory)))
|
|
(false-if-exception (delete-file (test-file)))
|
|
|
|
(pass-if-equal "current working directory and port" '(#f "hello")
|
|
(skip-if-unsupported)
|
|
(mkdir (test-directory))
|
|
;; Create a file in the working directory
|
|
(call-with-output-file (test-file)
|
|
(lambda (port) (display "hello" port)))
|
|
(let ((port (open (test-directory) O_RDONLY)))
|
|
;; Try to rename it
|
|
(rename-file-at #f (basename (test-file)) port "b")
|
|
(close-port port)
|
|
;; Verify it exists under the new name, and not under the old name.
|
|
(list (file-exists? (test-file))
|
|
(call-with-input-file (in-vicinity (test-directory) "b")
|
|
get-string-all))))
|
|
|
|
(false-if-exception (delete-file (in-vicinity (test-directory) "b")))
|
|
(false-if-exception (delete-file (test-file)))
|
|
(false-if-exception (rmdir (test-directory)))
|
|
|
|
(pass-if-exception "not a file port (1)" exception:wrong-type-arg
|
|
(skip-if-unsupported)
|
|
(rename-file-at (open-input-string "") "some" #f "thing"))
|
|
|
|
(pass-if-exception "not a file port (2)" exception:wrong-type-arg
|
|
(skip-if-unsupported)
|
|
(rename-file-at #f "some" (open-input-string "") "thing"))
|
|
|
|
(pass-if-exception "closed port (1)" exception:wrong-type-arg
|
|
(skip-if-unsupported)
|
|
(rename-file-at (call-with-port (open "." O_RDONLY) identity)
|
|
"some" #f "thing"))
|
|
|
|
(pass-if-exception "closed port (2)" exception:wrong-type-arg
|
|
(skip-if-unsupported)
|
|
(rename-file-at #f "some" (call-with-port (open "." O_RDONLY) identity)
|
|
"thing"))
|
|
|
|
(pass-if-exception "not a string (1)" exception:wrong-type-arg
|
|
(skip-if-unsupported)
|
|
(rename-file-at #f 'what #f "thing"))
|
|
|
|
(pass-if-exception "not a string (2)" exception:wrong-type-arg
|
|
(skip-if-unsupported)
|
|
(rename-file-at #f "some" #f 'what)))
|
|
|
|
(with-test-prefix "delete-file-at"
|
|
(define (skip-if-unsupported)
|
|
(when (not (and (defined? 'delete-file-at)
|
|
(defined? 'AT_REMOVEDIR)))
|
|
(throw 'unsupported)))
|
|
(define (create-test-file)
|
|
(call-with-output-file (test-file) identity))
|
|
(define (create-test-directory)
|
|
(mkdir (test-directory)))
|
|
(define (delete-test-file)
|
|
(when (file-exists? (test-file))
|
|
(delete-file (test-file))))
|
|
(define (delete-test-directory)
|
|
(when (file-exists? (test-directory))
|
|
(rmdir (test-directory))))
|
|
|
|
(pass-if-equal "regular file" #f
|
|
(skip-if-unsupported)
|
|
(create-test-file)
|
|
(call-with-port
|
|
(open (dirname (test-file)) O_RDONLY)
|
|
(lambda (port)
|
|
(delete-file-at port (basename (test-file)))))
|
|
(file-exists? (test-file)))
|
|
(delete-test-file)
|
|
|
|
(pass-if-equal "regular file, explicit flags" #f
|
|
(skip-if-unsupported)
|
|
(create-test-file)
|
|
(call-with-port
|
|
(open (dirname (test-file)) O_RDONLY)
|
|
(lambda (port)
|
|
(delete-file-at port (basename (test-file)) 0)))
|
|
(file-exists? (test-file)))
|
|
(delete-test-file)
|
|
|
|
(pass-if-equal "directory, explicit flags" #f
|
|
(skip-if-unsupported)
|
|
(create-test-directory)
|
|
(call-with-port
|
|
(open (dirname (test-directory)) O_RDONLY)
|
|
(lambda (port)
|
|
(delete-file-at port (basename (test-directory)) AT_REMOVEDIR)))
|
|
(file-exists? (test-directory)))
|
|
(delete-test-directory)
|
|
|
|
(pass-if-exception "not a port" exception:wrong-type-arg
|
|
(skip-if-unsupported)
|
|
(delete-file-at 'bogus "irrelevant"))
|
|
|
|
(pass-if-exception "not a file port" exception:wrong-type-arg
|
|
(skip-if-unsupported)
|
|
(delete-file-at (open-input-string "") "irrelevant"))
|
|
|
|
(pass-if-exception "closed port" exception:wrong-type-arg
|
|
(skip-if-unsupported)
|
|
(delete-file-at (call-with-port (open "." O_RDONLY) identity)
|
|
"irrelevant")))
|
|
|
|
(with-test-prefix "openat"
|
|
(define (skip-if-unsupported)
|
|
(unless (defined? 'openat)
|
|
(throw 'unsupported)))
|
|
|
|
(define file (search-path %load-path "ice-9/boot-9.scm"))
|
|
|
|
(define (call-with-relatively-opened-file directory-arguments file-arguments
|
|
proc)
|
|
(call-with-port
|
|
(apply open directory-arguments)
|
|
(lambda (directory)
|
|
(call-with-port
|
|
(apply openat directory file-arguments)
|
|
(lambda (port)
|
|
(proc port))))))
|
|
|
|
(pass-if-equal "mode read-only" "r"
|
|
(skip-if-unsupported)
|
|
(call-with-relatively-opened-file
|
|
(list (dirname file) O_RDONLY)
|
|
(list (basename file) O_RDONLY)
|
|
(lambda (port) (port-mode port))))
|
|
|
|
(pass-if-equal "port-revealed count" 0
|
|
(skip-if-unsupported)
|
|
(call-with-relatively-opened-file
|
|
(list (dirname file) O_RDONLY)
|
|
(list (basename file) O_RDONLY)
|
|
(lambda (port) (port-revealed port))))
|
|
|
|
(when (file-exists? (test-file))
|
|
(delete-file (test-file)))
|
|
|
|
(pass-if-equal "O_CREAT/O_WRONLY" (list #t (logand (lognot (umask)) #o666) "w")
|
|
(skip-if-unsupported)
|
|
(call-with-relatively-opened-file
|
|
(list (dirname (test-file)) O_RDONLY)
|
|
(list (basename (test-file)) (logior O_WRONLY O_CREAT))
|
|
(lambda (port)
|
|
(list (file-exists? (test-file))
|
|
(stat:perms (stat (test-file)))
|
|
(port-mode port)))))
|
|
|
|
(when (file-exists? (test-file))
|
|
(delete-file (test-file)))
|
|
|
|
(pass-if-equal "O_CREAT/O_WRONLY, non-default mode"
|
|
(list #t (logand (lognot (umask)) #o700) "w")
|
|
(skip-if-unsupported)
|
|
(call-with-relatively-opened-file
|
|
(list (dirname (test-file)) O_RDONLY)
|
|
(list (basename (test-file)) (logior O_WRONLY O_CREAT) #o700)
|
|
(lambda (port)
|
|
(list (file-exists? (test-file))
|
|
(stat:perms (stat (test-file)))
|
|
(port-mode port)))))
|
|
|
|
(pass-if-exception "closed port" exception:wrong-type-arg
|
|
(skip-if-unsupported)
|
|
(openat (call-with-port (open "." O_RDONLY) identity) "." O_RDONLY))
|
|
|
|
(pass-if-exception "non-file port" exception:wrong-type-arg
|
|
(skip-if-unsupported)
|
|
(openat (open-input-string "") "." O_RDONLY))
|
|
|
|
(pass-if-exception "not a port" exception:wrong-type-arg
|
|
(skip-if-unsupported)
|
|
(openat "not a port" "." O_RDONLY))
|
|
|
|
(when (file-exists? (test-file))
|
|
(delete-file (test-file))))
|