filesys.test 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811
  1. ;;;; filesys.test --- test file system functions -*- scheme -*-
  2. ;;;;
  3. ;;;; Copyright (C) 2004, 2006, 2013, 2019, 2021 Free Software Foundation, Inc.
  4. ;;;; Copyright (C) 2021 Maxime Devos <maximedevos@telenet.be>
  5. ;;;;
  6. ;;;; This library is free software; you can redistribute it and/or
  7. ;;;; modify it under the terms of the GNU Lesser General Public
  8. ;;;; License as published by the Free Software Foundation; either
  9. ;;;; version 3 of the License, or (at your option) any later version.
  10. ;;;;
  11. ;;;; This library is distributed in the hope that it will be useful,
  12. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. ;;;; Lesser General Public License for more details.
  15. ;;;;
  16. ;;;; You should have received a copy of the GNU Lesser General Public
  17. ;;;; License along with this library; if not, write to the Free Software
  18. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  19. (define-module (test-suite test-filesys)
  20. #:use-module (test-suite lib)
  21. #:use-module (test-suite guile-test)
  22. #:use-module (ice-9 threads)
  23. #:use-module (ice-9 match)
  24. #:use-module (rnrs io ports)
  25. #:use-module (rnrs bytevectors))
  26. (define (test-file)
  27. (data-file-name "filesys-test.tmp"))
  28. (define (test-symlink)
  29. (data-file-name "filesys-test-link.tmp"))
  30. (define (test-directory)
  31. (data-file-name "filesys-test-dir.tmp"))
  32. (define (test-directory2)
  33. (data-file-name "filesys-test-dir2.tmp"))
  34. ;;;
  35. ;;; copy-file
  36. ;;;
  37. (with-test-prefix "copy-file"
  38. ;; return next prospective file descriptor number
  39. (define (next-fd)
  40. (let ((fd (dup 0)))
  41. (close fd)
  42. fd))
  43. ;; in guile 1.6.4 and earlier, copy-file didn't close the input fd when
  44. ;; the output could not be opened
  45. (pass-if "fd leak when dest unwritable"
  46. (let ((old-next (next-fd)))
  47. (false-if-exception (copy-file "/dev/null" "no/such/dir/foo"))
  48. (= old-next (next-fd)))))
  49. ;;;
  50. ;;; lstat
  51. ;;;
  52. (with-test-prefix "lstat"
  53. (pass-if "normal file"
  54. (call-with-output-file (test-file)
  55. (lambda (port)
  56. (display "hello" port)))
  57. (eqv? 5 (stat:size (lstat (test-file)))))
  58. (call-with-output-file (test-file)
  59. (lambda (port)
  60. (display "hello" port)))
  61. (false-if-exception (delete-file (test-symlink)))
  62. (if (not (false-if-exception
  63. (begin (symlink (test-file) (test-symlink)) #t)))
  64. (display "cannot create symlink, lstat test skipped\n")
  65. (pass-if "symlink"
  66. ;; not much to test, except that it works
  67. (->bool (lstat (test-symlink))))))
  68. ;;;
  69. ;;; opendir and friends
  70. ;;;
  71. (with-test-prefix "opendir"
  72. (with-test-prefix "root directory"
  73. (let ((d (opendir "/")))
  74. (pass-if "not empty"
  75. (string? (readdir d)))
  76. (pass-if "all entries are strings"
  77. (let more ()
  78. (let ((f (readdir d)))
  79. (cond ((string? f)
  80. (more))
  81. ((eof-object? f)
  82. #t)
  83. (else
  84. #f)))))
  85. (closedir d))))
  86. ;;;
  87. ;;; stat
  88. ;;;
  89. (with-test-prefix "stat"
  90. (with-test-prefix "filename"
  91. (pass-if "size"
  92. (call-with-output-file (test-file)
  93. (lambda (port)
  94. (display "hello" port)))
  95. (eqv? 5 (stat:size (stat (test-file))))))
  96. (with-test-prefix "file descriptor"
  97. (pass-if "size"
  98. (call-with-output-file (test-file)
  99. (lambda (port)
  100. (display "hello" port)))
  101. (let* ((fd (open-fdes (test-file) O_RDONLY))
  102. (st (stat fd)))
  103. (close-fdes fd)
  104. (eqv? 5 (stat:size st)))))
  105. (with-test-prefix "port"
  106. (pass-if "size"
  107. (call-with-output-file (test-file)
  108. (lambda (port)
  109. (display "hello" port)))
  110. (let* ((port (open-file (test-file) "r+"))
  111. (st (stat port)))
  112. (close-port port)
  113. (eqv? 5 (stat:size st))))))
  114. (with-test-prefix "statat"
  115. ;; file-exists? from (ice-9 boot) dereferences symbolic links
  116. ;; (a bug?).
  117. (define (file-exists? filename)
  118. (catch 'system-error
  119. (lambda () (lstat filename) #t)
  120. (lambda args
  121. (if (= (system-error-errno args) ENOENT)
  122. ;; For the purposes of the following tests,
  123. ;; it is safe to ignore errors like EPERM, but a correct
  124. ;; implementation would return #t for that error.
  125. #f
  126. (apply throw args)))))
  127. (define (maybe-delete-directory)
  128. (when (file-exists? (test-directory))
  129. (for-each
  130. (lambda (filename)
  131. (define full-name (in-vicinity (test-directory) filename))
  132. (when (file-exists? full-name)
  133. (delete-file full-name)))
  134. '("test-file" "test-symlink"))
  135. (rmdir (test-directory))))
  136. (define (skip-unless-defined . things)
  137. (for-each (lambda (thing)
  138. (unless (defined? thing)
  139. (throw 'unsupported)))
  140. things))
  141. (maybe-delete-directory)
  142. (mkdir (test-directory))
  143. (call-with-output-file (in-vicinity (test-directory) "test-file")
  144. (lambda (port)
  145. (display "hello" port)))
  146. ;; Return #true if the symlink was created, #false otherwise.
  147. (define (maybe-create-symlink)
  148. (if (file-exists? (in-vicinity (test-directory) "test-symlink"))
  149. #t
  150. (false-if-exception
  151. (symlink "test-file"
  152. (in-vicinity (test-directory) "test-symlink")))))
  153. (pass-if-equal "regular file" 5
  154. (skip-unless-defined 'statat)
  155. (call-with-port
  156. (open (test-directory) O_RDONLY)
  157. (lambda (port)
  158. (stat:size (statat port "test-file")))))
  159. (pass-if-equal "regular file, AT_SYMLINK_NOFOLLOW" 5
  160. (skip-unless-defined 'statat 'AT_SYMLINK_NOFOLLOW)
  161. (call-with-port
  162. (open (test-directory) O_RDONLY)
  163. (lambda (port)
  164. (stat:size (statat port "test-file" AT_SYMLINK_NOFOLLOW)))))
  165. (pass-if-equal "symbolic links are dereferenced" '(regular 5)
  166. ;; Not all systems support symlinks.
  167. (skip-unless-defined 'statat 'symlink)
  168. (unless (maybe-create-symlink)
  169. (throw 'unresolved))
  170. (call-with-port
  171. (open (test-directory) O_RDONLY)
  172. (lambda (port)
  173. (define result (statat port "test-symlink"))
  174. (list (stat:type result) (stat:size result)))))
  175. (pass-if-equal "symbolic links are not dereferenced"
  176. `(symlink ,(string-length "test-file"))
  177. ;; Not all systems support symlinks.
  178. (skip-unless-defined 'statat 'symlink)
  179. (unless (maybe-create-symlink)
  180. (throw 'unresolved))
  181. (call-with-port
  182. (open (test-directory) O_RDONLY)
  183. (lambda (port)
  184. (define result (statat port "test-symlink" AT_SYMLINK_NOFOLLOW))
  185. (list (stat:type result) (stat:size result)))))
  186. (maybe-delete-directory))
  187. (with-test-prefix "sendfile"
  188. (let* ((file (search-path %load-path "ice-9/boot-9.scm"))
  189. (len (stat:size (stat file)))
  190. (ref (call-with-input-file file get-bytevector-all)))
  191. (pass-if-equal "file" (cons len ref)
  192. (let* ((result (call-with-input-file file
  193. (lambda (input)
  194. (call-with-output-file (test-file)
  195. (lambda (output)
  196. (sendfile output input len 0))))))
  197. (out (call-with-input-file (test-file) get-bytevector-all)))
  198. (cons result out)))
  199. (pass-if-equal "file with offset"
  200. (cons (- len 777) (call-with-input-file file
  201. (lambda (input)
  202. (seek input 777 SEEK_SET)
  203. (get-bytevector-all input))))
  204. (let* ((result (call-with-input-file file
  205. (lambda (input)
  206. (call-with-output-file (test-file)
  207. (lambda (output)
  208. (sendfile output input (- len 777) 777))))))
  209. (out (call-with-input-file (test-file) get-bytevector-all)))
  210. (cons result out)))
  211. (pass-if-equal "file with offset past the end"
  212. (cons (- len 777) (call-with-input-file file
  213. (lambda (input)
  214. (seek input 777 SEEK_SET)
  215. (get-bytevector-all input))))
  216. (let* ((result (call-with-input-file file
  217. (lambda (input)
  218. (call-with-output-file (test-file)
  219. (lambda (output)
  220. (sendfile output input len 777))))))
  221. (out (call-with-input-file (test-file) get-bytevector-all)))
  222. (cons result out)))
  223. (pass-if-equal "file with offset near the end"
  224. (cons 77 (call-with-input-file file
  225. (lambda (input)
  226. (seek input (- len 77) SEEK_SET)
  227. (get-bytevector-all input))))
  228. (let* ((result (call-with-input-file file
  229. (lambda (input)
  230. (call-with-output-file (test-file)
  231. (lambda (output)
  232. (sendfile output input len (- len 77)))))))
  233. (out (call-with-input-file (test-file) get-bytevector-all)))
  234. (cons result out)))
  235. (pass-if-equal "pipe" (cons len ref)
  236. (if (provided? 'threads)
  237. (let* ((in+out (pipe))
  238. (child (call-with-new-thread
  239. (lambda ()
  240. (call-with-input-file file
  241. (lambda (input)
  242. (let ((result (sendfile (cdr in+out)
  243. (fileno input)
  244. len 0)))
  245. (close-port (cdr in+out))
  246. result)))))))
  247. (let ((out (get-bytevector-all (car in+out))))
  248. (close-port (car in+out))
  249. (cons (join-thread child) out)))
  250. (throw 'unresolved)))
  251. (pass-if-equal "pipe with offset"
  252. (cons (- len 777) (call-with-input-file file
  253. (lambda (input)
  254. (seek input 777 SEEK_SET)
  255. (get-bytevector-all input))))
  256. (if (provided? 'threads)
  257. (let* ((in+out (pipe))
  258. (child (call-with-new-thread
  259. (lambda ()
  260. (call-with-input-file file
  261. (lambda (input)
  262. (let ((result (sendfile (cdr in+out)
  263. (fileno input)
  264. (- len 777)
  265. 777)))
  266. (close-port (cdr in+out))
  267. result)))))))
  268. (let ((out (get-bytevector-all (car in+out))))
  269. (close-port (car in+out))
  270. (cons (join-thread child) out)))
  271. (throw 'unresolved)))))
  272. (with-test-prefix "basename"
  273. (pass-if-equal "/" "/" (basename "/"))
  274. (pass-if-equal "//" "/" (basename "//"))
  275. (pass-if-equal "a/b/c" "c" (basename "a/b/c")))
  276. (delete-file (test-file))
  277. (when (file-exists? (test-symlink))
  278. (delete-file (test-symlink)))
  279. (with-test-prefix "mkdtemp"
  280. (pass-if-exception "number arg" exception:wrong-type-arg
  281. (if (not (defined? 'mkdtemp))
  282. (throw 'unresolved)
  283. (mkdtemp 123)))
  284. (pass-if "template prefix is preserved"
  285. (if (not (defined? 'mkdtemp))
  286. (throw 'unresolved)
  287. (let* ((template "T-XXXXXX")
  288. (name (mkdtemp template)))
  289. (false-if-exception (rmdir name))
  290. (and
  291. (string? name)
  292. (string-contains name "T-")
  293. (= (string-length name) 8)))))
  294. (pass-if-exception "invalid template" exception:system-error
  295. (if (not (defined? 'mkdtemp))
  296. (throw 'unresolved)
  297. (mkdtemp "T-AAAAAA")))
  298. (pass-if "directory created"
  299. (if (not (defined? 'mkdtemp))
  300. (throw 'unresolved)
  301. (let* ((template "T-XXXXXX")
  302. (name (mkdtemp template)))
  303. (let* ((_stat (stat name))
  304. (result (eqv? 'directory (stat:type _stat))))
  305. (false-if-exception (rmdir name))
  306. result)))))
  307. ;;;
  308. ;;; chmodat
  309. ;;;
  310. (with-test-prefix "chmodat"
  311. (call-with-output-file (test-file) (const #f))
  312. (chmod (test-file) #o000)
  313. (pass-if-equal "regular file"
  314. #o300
  315. (unless (defined? 'chmodat)
  316. (throw 'unsupported))
  317. (call-with-port
  318. (open (dirname (test-file)) O_RDONLY)
  319. (lambda (port)
  320. (chmodat port (test-file) #o300)))
  321. (stat:perms (stat (test-file))))
  322. (chmod (test-file) #o000)
  323. (pass-if-equal "regular file, AT_SYMLINK_NOFOLLOW"
  324. #o300
  325. (unless (and (defined? 'chmodat)
  326. (defined? 'AT_SYMLINK_NOFOLLOW))
  327. (throw 'unsupported))
  328. (call-with-port
  329. (open (dirname (test-file)) O_RDONLY)
  330. (lambda (port)
  331. (catch 'system-error
  332. (lambda ()
  333. (chmodat port (basename (test-file)) #o300 AT_SYMLINK_NOFOLLOW))
  334. (lambda args
  335. (close-port port)
  336. ;; AT_SYMLINK_NOFOLLOW is not supported on Linux (at least Linux
  337. ;; 5.11.2 with the btrfs file system), even for regular files.
  338. (if (= ENOTSUP (system-error-errno args))
  339. (begin
  340. (display "fchmodat doesn't support AT_SYMLINK_NOFOLLOW\n")
  341. (throw 'unresolved))
  342. (apply throw args))))))
  343. (stat:perms (stat (test-file))))
  344. (pass-if-exception "not a port" exception:wrong-type-arg
  345. (unless (defined? 'chmodat)
  346. (throw 'unsupported))
  347. (chmodat "bogus" (test-file) #o300))
  348. (pass-if-exception "not a file port" exception:wrong-type-arg
  349. (unless (defined? 'chmodat)
  350. (throw 'unsupported))
  351. (chmodat (open-input-string "") (test-file) #o300))
  352. (pass-if-exception "closed port" exception:wrong-type-arg
  353. (unless (defined? 'chmodat)
  354. (throw 'unsupported))
  355. (chmodat (call-with-port (open "." O_RDONLY) identity) (test-file) #o300))
  356. (chmod (test-file) #o600)
  357. (delete-file (test-file)))
  358. (with-test-prefix "chdir"
  359. (pass-if-equal "current directory" (getcwd)
  360. (begin (chdir ".") (getcwd)))
  361. (define file (search-path %load-path "ice-9/boot-9.scm"))
  362. (pass-if-equal "test directory" (dirname file)
  363. (let ((olddir (getcwd))
  364. (dir #f))
  365. (chdir (dirname file))
  366. (set! dir (getcwd))
  367. (chdir olddir)
  368. dir))
  369. (pass-if-equal "test directory, via port" (dirname file)
  370. (unless (provided? 'chdir-port)
  371. (throw 'unresolved))
  372. (let ((olddir (getcwd))
  373. (port (open (dirname file) O_RDONLY))
  374. (dir #f))
  375. (chdir port)
  376. (set! dir (getcwd))
  377. (chdir olddir)
  378. dir))
  379. (pass-if-exception "closed port" exception:wrong-type-arg
  380. (unless (provided? 'chdir-port)
  381. (throw 'unresolved))
  382. (let ((port (open (dirname file) O_RDONLY))
  383. (olddir (getcwd)))
  384. (close-port port)
  385. (chdir port)
  386. (chdir olddir))) ; should not be reached
  387. (pass-if-exception "not a port or file name" exception:wrong-type-arg
  388. (chdir '(stuff)))
  389. (pass-if-exception "non-file port" exception:wrong-type-arg
  390. (chdir (open-input-string ""))))
  391. (with-test-prefix "readlink"
  392. (false-if-exception (delete-file (test-symlink)))
  393. (false-if-exception (delete-file (test-file)))
  394. (call-with-output-file (test-file)
  395. (lambda (port)
  396. (display "hello" port)))
  397. (if (not (false-if-exception
  398. (begin (symlink (test-file) (test-symlink)) #t)))
  399. (display "cannot create symlink, some readlink tests skipped\n")
  400. (let ()
  401. (pass-if-equal "file name of symlink" (test-file)
  402. (readlink (test-symlink)))
  403. (pass-if-equal "port representing a symlink" (test-file)
  404. (let ()
  405. (unless (and (provided? 'readlink-port)
  406. (defined? 'O_NOFOLLOW)
  407. (defined? 'O_PATH)
  408. (not (= 0 O_NOFOLLOW))
  409. (not (= 0 O_PATH)))
  410. (throw 'unsupported))
  411. (define port (open (test-symlink) (logior O_NOFOLLOW O_PATH)))
  412. (define points-to (false-if-exception (readlink port)))
  413. (close-port port)
  414. points-to))
  415. (pass-if-exception "not a port or file name" exception:wrong-type-arg
  416. (readlink '(stuff)))))
  417. (pass-if-equal "port representing a regular file" EINVAL
  418. (unless (provided? 'readlink-port)
  419. (throw 'unsupported))
  420. (call-with-input-file (test-file)
  421. (lambda (port)
  422. (catch 'system-error
  423. (lambda ()
  424. (readlink port)
  425. (close-port port) ; should be unreachable
  426. #f)
  427. (lambda args
  428. (close-port port)
  429. ;; At least Linux 5.10.46 returns ENOENT instead of EINVAL.
  430. ;; Possibly surprising, but it is documented in some man
  431. ;; pages and it doesn't appear to be an accident:
  432. ;; <https://elixir.bootlin.com/linux/v5.10.46/source/fs/stat.c#L419>.
  433. (define error (system-error-errno args))
  434. (if (= error ENOENT)
  435. EINVAL
  436. error))))))
  437. (pass-if-exception "non-file port" exception:wrong-type-arg
  438. (readlink (open-input-string "")))
  439. (pass-if-exception "closed port" exception:wrong-type-arg
  440. (let ((port (open-file (test-file) "r")))
  441. (close-port port)
  442. (readlink port)))
  443. (false-if-exception (delete-file (test-symlink)))
  444. (false-if-exception (delete-file (test-file))))
  445. (with-test-prefix "symlinkat"
  446. (pass-if-equal "create" (test-file)
  447. (unless (defined? 'symlinkat)
  448. (throw 'unsupported))
  449. (call-with-port
  450. (open "." O_RDONLY)
  451. (lambda (port)
  452. (symlinkat port (test-file) (test-symlink))
  453. (readlink (test-symlink)))))
  454. (false-if-exception (delete-file (test-symlink)))
  455. (pass-if-exception "not a port" exception:wrong-type-arg
  456. (unless (defined? 'symlinkat)
  457. (throw 'unsupported))
  458. (symlinkat "bogus" (test-file) (test-symlink)))
  459. (pass-if-exception "not a file port" exception:wrong-type-arg
  460. (unless (defined? 'symlinkat)
  461. (throw 'unsupported))
  462. (symlinkat (open-input-string "") (test-file) (test-symlink)))
  463. (pass-if-exception "closed port" exception:wrong-type-arg
  464. (unless (defined? 'symlinkat)
  465. (throw 'unsupported))
  466. (symlinkat (call-with-port (open "." O_RDONLY) identity)
  467. (test-file) (test-symlink))))
  468. (with-test-prefix "mkdirat"
  469. (define (skip-if-unsupported)
  470. (unless (defined? 'mkdirat)
  471. (throw 'unsupported)))
  472. (define (maybe-delete-directory)
  473. (when (file-exists? (test-directory))
  474. (rmdir (test-directory))))
  475. (maybe-delete-directory)
  476. (pass-if-equal "create" 'directory
  477. (skip-if-unsupported)
  478. (call-with-port
  479. (open "." O_RDONLY)
  480. (lambda (port)
  481. (mkdirat port (test-directory))
  482. (stat:type (stat (test-directory))))))
  483. (maybe-delete-directory)
  484. (pass-if-equal "explicit perms" (logand #o111 (lognot (umask)))
  485. (skip-if-unsupported)
  486. (call-with-port
  487. (open "." O_RDONLY)
  488. (lambda (port)
  489. (mkdirat port (test-directory) #o111)
  490. (stat:perms (stat (test-directory))))))
  491. (maybe-delete-directory)
  492. (pass-if-equal "create, implicit perms" (logand #o777 (lognot (umask)))
  493. (skip-if-unsupported)
  494. (call-with-port
  495. (open "." O_RDONLY)
  496. (lambda (port)
  497. (mkdirat port (test-directory))
  498. (stat:perms (stat (test-directory))))))
  499. (maybe-delete-directory))
  500. (with-test-prefix "rename-file-at"
  501. (define (skip-if-unsupported)
  502. (unless (defined? 'rename-file-at)
  503. (throw 'unsupported)))
  504. (pass-if-equal "current working directory" '(#f "hello")
  505. (skip-if-unsupported)
  506. ;; Create a file in the test directory
  507. (call-with-output-file "filesys-test-a.tmp"
  508. (lambda (port) (display "hello" port)))
  509. ;; Try to rename it
  510. (rename-file-at #f "filesys-test-a.tmp" #f "filesys-test-b.tmp")
  511. ;; Verify it exists under the new name, and not under the old name
  512. (list (file-exists? "filesys-test-a.tmp")
  513. (call-with-input-file "filesys-test-b.tmp" get-string-all)))
  514. (false-if-exception (delete-file "filesys-test-a.tmp"))
  515. (false-if-exception (delete-file "filesys-test-b.tmp"))
  516. (pass-if-equal "two ports" '(#f "hello")
  517. (skip-if-unsupported)
  518. (mkdir (test-directory))
  519. (mkdir (test-directory2))
  520. ;; Create a file in the first directory
  521. (call-with-output-file (in-vicinity (test-directory) "a")
  522. (lambda (port) (display "hello" port)))
  523. (let ((port1 (open (test-directory) O_RDONLY))
  524. (port2 (open (test-directory2) O_RDONLY)))
  525. ;; Try to rename it
  526. (rename-file-at port1 "a" port2 "b")
  527. (close-port port1)
  528. (close-port port2)
  529. ;; Verify it exists under the new name, and not under the old name
  530. (list (file-exists? (in-vicinity (test-directory) "a"))
  531. (call-with-input-file (in-vicinity (test-directory2) "b")
  532. get-string-all))))
  533. (false-if-exception (delete-file (in-vicinity (test-directory) "a")))
  534. (false-if-exception (delete-file (in-vicinity (test-directory2) "b")))
  535. (false-if-exception (rmdir (test-directory)))
  536. (false-if-exception (rmdir (test-directory2)))
  537. (pass-if-equal "port and current working directory" '(#f "hello")
  538. (skip-if-unsupported)
  539. (mkdir (test-directory))
  540. ;; Create a file in (test-directory)
  541. (call-with-output-file (in-vicinity (test-directory) "a")
  542. (lambda (port) (display "hello" port)))
  543. (let ((port (open (test-directory) O_RDONLY)))
  544. ;; Try to rename it
  545. (rename-file-at port "a" #f (basename (test-file)))
  546. (close-port port)
  547. ;; Verify it exists under the new name, and not under the old name.
  548. (list (file-exists? (in-vicinity (test-directory) "a"))
  549. (call-with-input-file (test-file) get-string-all))))
  550. (false-if-exception (delete-file (in-vicinity (test-directory) "a")))
  551. (false-if-exception (rmdir (test-directory)))
  552. (false-if-exception (delete-file (test-file)))
  553. (pass-if-equal "current working directory and port" '(#f "hello")
  554. (skip-if-unsupported)
  555. (mkdir (test-directory))
  556. ;; Create a file in the working directory
  557. (call-with-output-file (test-file)
  558. (lambda (port) (display "hello" port)))
  559. (let ((port (open (test-directory) O_RDONLY)))
  560. ;; Try to rename it
  561. (rename-file-at #f (basename (test-file)) port "b")
  562. (close-port port)
  563. ;; Verify it exists under the new name, and not under the old name.
  564. (list (file-exists? (test-file))
  565. (call-with-input-file (in-vicinity (test-directory) "b")
  566. get-string-all))))
  567. (false-if-exception (delete-file (in-vicinity (test-directory) "b")))
  568. (false-if-exception (delete-file (test-file)))
  569. (false-if-exception (rmdir (test-directory)))
  570. (pass-if-exception "not a file port (1)" exception:wrong-type-arg
  571. (skip-if-unsupported)
  572. (rename-file-at (open-input-string "") "some" #f "thing"))
  573. (pass-if-exception "not a file port (2)" exception:wrong-type-arg
  574. (skip-if-unsupported)
  575. (rename-file-at #f "some" (open-input-string "") "thing"))
  576. (pass-if-exception "closed port (1)" exception:wrong-type-arg
  577. (skip-if-unsupported)
  578. (rename-file-at (call-with-port (open "." O_RDONLY) identity)
  579. "some" #f "thing"))
  580. (pass-if-exception "closed port (2)" exception:wrong-type-arg
  581. (skip-if-unsupported)
  582. (rename-file-at #f "some" (call-with-port (open "." O_RDONLY) identity)
  583. "thing"))
  584. (pass-if-exception "not a string (1)" exception:wrong-type-arg
  585. (skip-if-unsupported)
  586. (rename-file-at #f 'what #f "thing"))
  587. (pass-if-exception "not a string (2)" exception:wrong-type-arg
  588. (skip-if-unsupported)
  589. (rename-file-at #f "some" #f 'what)))
  590. (with-test-prefix "delete-file-at"
  591. (define (skip-if-unsupported)
  592. (when (not (and (defined? 'delete-file-at)
  593. (defined? 'AT_REMOVEDIR)))
  594. (throw 'unsupported)))
  595. (define (create-test-file)
  596. (call-with-output-file (test-file) identity))
  597. (define (create-test-directory)
  598. (mkdir (test-directory)))
  599. (define (delete-test-file)
  600. (when (file-exists? (test-file))
  601. (delete-file (test-file))))
  602. (define (delete-test-directory)
  603. (when (file-exists? (test-directory))
  604. (rmdir (test-directory))))
  605. (pass-if-equal "regular file" #f
  606. (skip-if-unsupported)
  607. (create-test-file)
  608. (call-with-port
  609. (open (dirname (test-file)) O_RDONLY)
  610. (lambda (port)
  611. (delete-file-at port (basename (test-file)))))
  612. (file-exists? (test-file)))
  613. (delete-test-file)
  614. (pass-if-equal "regular file, explicit flags" #f
  615. (skip-if-unsupported)
  616. (create-test-file)
  617. (call-with-port
  618. (open (dirname (test-file)) O_RDONLY)
  619. (lambda (port)
  620. (delete-file-at port (basename (test-file)) 0)))
  621. (file-exists? (test-file)))
  622. (delete-test-file)
  623. (pass-if-equal "directory, explicit flags" #f
  624. (skip-if-unsupported)
  625. (create-test-directory)
  626. (call-with-port
  627. (open (dirname (test-directory)) O_RDONLY)
  628. (lambda (port)
  629. (delete-file-at port (basename (test-directory)) AT_REMOVEDIR)))
  630. (file-exists? (test-directory)))
  631. (delete-test-directory)
  632. (pass-if-exception "not a port" exception:wrong-type-arg
  633. (skip-if-unsupported)
  634. (delete-file-at 'bogus "irrelevant"))
  635. (pass-if-exception "not a file port" exception:wrong-type-arg
  636. (skip-if-unsupported)
  637. (delete-file-at (open-input-string "") "irrelevant"))
  638. (pass-if-exception "closed port" exception:wrong-type-arg
  639. (skip-if-unsupported)
  640. (delete-file-at (call-with-port (open "." O_RDONLY) identity)
  641. "irrelevant")))
  642. (with-test-prefix "openat"
  643. (define (skip-if-unsupported)
  644. (unless (defined? 'openat)
  645. (throw 'unsupported)))
  646. (define file (search-path %load-path "ice-9/boot-9.scm"))
  647. (define (call-with-relatively-opened-file directory-arguments file-arguments
  648. proc)
  649. (call-with-port
  650. (apply open directory-arguments)
  651. (lambda (directory)
  652. (call-with-port
  653. (apply openat directory file-arguments)
  654. (lambda (port)
  655. (proc port))))))
  656. (pass-if-equal "mode read-only" "r"
  657. (skip-if-unsupported)
  658. (call-with-relatively-opened-file
  659. (list (dirname file) O_RDONLY)
  660. (list (basename file) O_RDONLY)
  661. (lambda (port) (port-mode port))))
  662. (pass-if-equal "port-revealed count" 0
  663. (skip-if-unsupported)
  664. (call-with-relatively-opened-file
  665. (list (dirname file) O_RDONLY)
  666. (list (basename file) O_RDONLY)
  667. (lambda (port) (port-revealed port))))
  668. (when (file-exists? (test-file))
  669. (delete-file (test-file)))
  670. (pass-if-equal "O_CREAT/O_WRONLY" (list #t (logand (lognot (umask)) #o666) "w")
  671. (skip-if-unsupported)
  672. (call-with-relatively-opened-file
  673. (list (dirname (test-file)) O_RDONLY)
  674. (list (basename (test-file)) (logior O_WRONLY O_CREAT))
  675. (lambda (port)
  676. (list (file-exists? (test-file))
  677. (stat:perms (stat (test-file)))
  678. (port-mode port)))))
  679. (when (file-exists? (test-file))
  680. (delete-file (test-file)))
  681. (pass-if-equal "O_CREAT/O_WRONLY, non-default mode"
  682. (list #t (logand (lognot (umask)) #o700) "w")
  683. (skip-if-unsupported)
  684. (call-with-relatively-opened-file
  685. (list (dirname (test-file)) O_RDONLY)
  686. (list (basename (test-file)) (logior O_WRONLY O_CREAT) #o700)
  687. (lambda (port)
  688. (list (file-exists? (test-file))
  689. (stat:perms (stat (test-file)))
  690. (port-mode port)))))
  691. (pass-if-exception "closed port" exception:wrong-type-arg
  692. (skip-if-unsupported)
  693. (openat (call-with-port (open "." O_RDONLY) identity) "." O_RDONLY))
  694. (pass-if-exception "non-file port" exception:wrong-type-arg
  695. (skip-if-unsupported)
  696. (openat (open-input-string "") "." O_RDONLY))
  697. (pass-if-exception "not a port" exception:wrong-type-arg
  698. (skip-if-unsupported)
  699. (openat "not a port" "." O_RDONLY))
  700. (when (file-exists? (test-file))
  701. (delete-file (test-file))))