dir.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. /*
  2. * Part of Scheme 48 1.9. See file COPYING for notices and license.
  3. *
  4. * Authors: Richard Kelsey, Jonathan Rees, Marcus Crestani, Mike Sperber,
  5. * Robert Ransom
  6. */
  7. /*
  8. * An interface to Unix opendir(), readdir(), closedir(),
  9. * stat() and lstat().
  10. * Note, readdir() returns #F on EOF.
  11. * Note, calling closedir on an already closed directory has no effect.
  12. * Note, readdir will never return "." or ".." (POSIX leaves this
  13. * unspecified).
  14. * Note, a stat object which is written out to a dump and used on a
  15. * different OS will cause problems because things have moved around.
  16. */
  17. #include <stdio.h>
  18. #include <stdarg.h>
  19. #include <stdlib.h>
  20. #include <unistd.h>
  21. #include <errno.h>
  22. #include <dirent.h>
  23. #include <time.h>
  24. #include <fcntl.h>
  25. #include <sys/types.h>
  26. #include <sys/stat.h>
  27. #include "scheme48.h"
  28. #include "scheme48vm.h" /* ps_close_fd() */
  29. #include "posix.h"
  30. #include "c-mods.h"
  31. #include "unix.h"
  32. #include "fd-io.h"
  33. extern void s48_init_posix_dir(void);
  34. static s48_ref_t posix_opendir(s48_call_t call, s48_ref_t svname),
  35. posix_closedir(s48_call_t call, s48_ref_t svdir),
  36. posix_readdir(s48_call_t call, s48_ref_t svdir),
  37. posix_working_directory(s48_call_t call, s48_ref_t new_wd),
  38. posix_open(s48_call_t call, s48_ref_t path, s48_ref_t id, s48_ref_t options,
  39. s48_ref_t mode, s48_ref_t input_p),
  40. posix_file_stuff(s48_call_t call, s48_ref_t op, s48_ref_t arg1,
  41. s48_ref_t arg2),
  42. posix_file_info(s48_call_t call,
  43. s48_ref_t os_str_name,
  44. s48_ref_t svname,
  45. s48_ref_t follow_link_p,
  46. s48_ref_t mode_enum),
  47. posix_create_symbolic_link(s48_call_t call,
  48. s48_ref_t svname1, s48_ref_t svname2),
  49. posix_read_symbolic_link(s48_call_t call, s48_ref_t svname);
  50. /*
  51. * Record types imported from Scheme.
  52. */
  53. static s48_ref_t posix_file_info_type_binding,
  54. posix_file_mode_type_binding,
  55. posix_user_id_type_binding;
  56. /*
  57. * Forward declarations.
  58. */
  59. static s48_ref_t enter_mode(s48_call_t call, mode_t mode);
  60. /*
  61. * Install all exported functions in Scheme48.
  62. */
  63. void
  64. s48_init_posix_dir(void)
  65. {
  66. S48_EXPORT_FUNCTION(posix_opendir);
  67. S48_EXPORT_FUNCTION(posix_readdir);
  68. S48_EXPORT_FUNCTION(posix_closedir);
  69. S48_EXPORT_FUNCTION(posix_working_directory);
  70. S48_EXPORT_FUNCTION(posix_open);
  71. S48_EXPORT_FUNCTION(posix_file_stuff);
  72. S48_EXPORT_FUNCTION(posix_file_info);
  73. S48_EXPORT_FUNCTION(posix_create_symbolic_link);
  74. S48_EXPORT_FUNCTION(posix_read_symbolic_link);
  75. posix_user_id_type_binding =
  76. s48_get_imported_binding_2("posix-user-id-type");
  77. posix_file_info_type_binding =
  78. s48_get_imported_binding_2("posix-file-info-type");
  79. posix_file_mode_type_binding =
  80. s48_get_imported_binding_2("posix-file-mode-type");
  81. }
  82. /*
  83. * Interface to opendir.
  84. */
  85. static s48_ref_t
  86. posix_opendir(s48_call_t call, s48_ref_t svname)
  87. {
  88. DIR *dp;
  89. s48_ref_t res;
  90. char *c_name;
  91. c_name = s48_extract_byte_vector_readonly_2(call, svname);
  92. RETRY_OR_RAISE_NULL(dp, opendir(c_name));
  93. res = s48_make_value_2(call, DIR *);
  94. s48_unsafe_extract_value_2(call, res, DIR *) = dp;
  95. return (res);
  96. }
  97. /*
  98. * Interface to closedir.
  99. * Note, it is ok to call closedir on an already closed directory.
  100. */
  101. static s48_ref_t
  102. posix_closedir(s48_call_t call, s48_ref_t svdir)
  103. {
  104. DIR **dpp;
  105. dpp = s48_extract_value_pointer_2(call, svdir, DIR *);
  106. if (*dpp != (DIR *)NULL) {
  107. int status;
  108. RETRY_OR_RAISE_NEG(status, closedir(*dpp));
  109. *dpp = (DIR *)NULL;
  110. }
  111. return s48_unspecific_2(call);
  112. }
  113. /*
  114. * Interface to readdir.
  115. * If we have already read all the files that are in the directory,
  116. * #F is returned. Otherwise, a string with the next file name.
  117. * Note, "." and ".." are never returned.
  118. */
  119. static s48_ref_t
  120. posix_readdir(s48_call_t call, s48_ref_t svdir)
  121. {
  122. DIR **dpp;
  123. struct dirent *dep;
  124. char *name;
  125. dpp = s48_extract_value_pointer_2(call, svdir, DIR *);
  126. if (*dpp == (DIR *)NULL)
  127. s48_assertion_violation_2(call, "posix_readdir", "invalid NULL value", 1, svdir);
  128. do {
  129. errno = 0;
  130. RETRY_NULL(dep, readdir(*dpp));
  131. if (dep == (struct dirent *)NULL) {
  132. if (errno != 0)
  133. s48_os_error_2(call, "posix_readdir", errno, 1, svdir);
  134. return s48_false_2(call);
  135. }
  136. name = dep->d_name;
  137. } while ((name[0] == '.')
  138. && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')));
  139. return s48_enter_byte_string_2(call, name);
  140. }
  141. /* ************************************************************ */
  142. /*
  143. * Working directory.
  144. *
  145. * If the name is a string, we set the working directory to it. If the name
  146. * is #f we return the current working directory. This would be trivial,
  147. * except that we don't know how big a buffer we need for the path. So we
  148. * keep trying until we run out of memory.
  149. */
  150. int going = 0;
  151. int second = 0;
  152. static s48_ref_t
  153. posix_working_directory(s48_call_t call, s48_ref_t new_wd)
  154. {
  155. if (second)
  156. going = 1;
  157. else
  158. second = 1;
  159. if (s48_false_p_2(call, new_wd)) {
  160. char *status;
  161. char stack_buffer[256];
  162. char *buffer = stack_buffer;
  163. int buffer_size = 256;
  164. while (0==0) {
  165. RETRY_NULL(status, getcwd(buffer, buffer_size));
  166. if (status == buffer) {
  167. s48_ref_t result = s48_enter_byte_string_2(call, buffer);
  168. if (buffer != stack_buffer)
  169. free(buffer);
  170. return result;
  171. }
  172. else if (errno == ERANGE) {
  173. if (buffer != stack_buffer)
  174. free(buffer);
  175. buffer_size *= 2;
  176. buffer = (char *) malloc(buffer_size * sizeof(char));
  177. if (buffer == NULL)
  178. s48_out_of_memory_error_2(call);
  179. }
  180. else
  181. s48_os_error("posix_working_directory", errno, 1, new_wd);
  182. }
  183. }
  184. else {
  185. int status;
  186. RETRY_OR_RAISE_NEG(status, chdir(s48_extract_byte_vector_readonly_2(call, new_wd)));
  187. return s48_unspecific_2(call);
  188. }
  189. }
  190. /* ************************************************************ */
  191. /*
  192. * Open() and friends.
  193. *
  194. */
  195. static s48_ref_t
  196. posix_open(s48_call_t call, s48_ref_t path, s48_ref_t id, s48_ref_t options, s48_ref_t mode, s48_ref_t input_p)
  197. {
  198. int fd,
  199. c_options;
  200. char *c_path;
  201. s48_ref_t channel;
  202. c_options = s48_extract_file_options(call, options);
  203. c_path = s48_extract_byte_vector_readonly_2(call, path);
  204. if ((O_WRONLY & c_options) || (O_RDWR & c_options))
  205. c_options |= O_NONBLOCK;
  206. if (s48_false_p_2(call, mode))
  207. RETRY_OR_RAISE_NEG(fd, open(c_path, c_options));
  208. else {
  209. mode_t c_mode = s48_extract_mode(call, mode);
  210. RETRY_OR_RAISE_NEG(fd, open(c_path, c_options, c_mode));
  211. }
  212. channel = s48_add_channel_2(call,
  213. s48_extract_boolean_2(call, input_p)
  214. ? s48_channel_status_input_2(call)
  215. : s48_channel_status_output_2(call),
  216. id,
  217. fd);
  218. if (!s48_channel_p_2(call, channel)) {
  219. ps_close_fd(fd); /* retries if interrupted */
  220. s48_raise_scheme_exception_2(call, s48_extract_long_2(call, channel), 0); };
  221. return channel;
  222. }
  223. /*
  224. * A bunch of simple procedures merged together to save typing.
  225. */
  226. static s48_ref_t
  227. posix_file_stuff(s48_call_t call, s48_ref_t op, s48_ref_t arg0, s48_ref_t arg1)
  228. {
  229. int status;
  230. switch (s48_extract_long_2(call, op)) {
  231. /* umask(new_mask) */
  232. case 0:
  233. return enter_mode(call, umask(s48_extract_mode(call, arg0)));
  234. /* link(existing, new) */
  235. case 1:
  236. RETRY_OR_RAISE_NEG(status, link(s48_extract_byte_vector_readonly_2(call, arg0),
  237. s48_extract_byte_vector_readonly_2(call, arg1)));
  238. break;
  239. /* mkdir(path, mode) */
  240. case 2:
  241. RETRY_OR_RAISE_NEG(status, mkdir(s48_extract_byte_vector_readonly_2(call, arg0),
  242. s48_extract_mode(call, arg1)));
  243. break;
  244. /* mkfifo(path, mode) */
  245. case 3:
  246. RETRY_OR_RAISE_NEG(status, mkfifo(s48_extract_byte_vector_readonly_2(call, arg0),
  247. s48_extract_mode(call, arg1)));
  248. break;
  249. /* unlink(char *path) */
  250. case 4:
  251. RETRY_OR_RAISE_NEG(status, unlink(s48_extract_byte_vector_readonly_2(call, arg0)));
  252. break;
  253. /* rmdir(char *path) */
  254. case 5:
  255. RETRY_OR_RAISE_NEG(status, rmdir(s48_extract_byte_vector_readonly_2(call, arg0)));
  256. break;
  257. /* rename(char *old, char *new) */
  258. case 6:
  259. RETRY_OR_RAISE_NEG(status, rename(s48_extract_byte_vector_readonly_2(call, arg0),
  260. s48_extract_byte_vector_readonly_2(call, arg1)));
  261. break;
  262. /* access(char *path, int modes) */
  263. case 7: {
  264. int modes = s48_extract_long_2(call, arg1);
  265. int local_modes = (001 & modes ? R_OK : 0) |
  266. (002 & modes ? W_OK : 0) |
  267. (004 & modes ? X_OK : 0) |
  268. (010 & modes ? F_OK : 0);
  269. char *path = s48_extract_byte_vector_readonly_2(call, arg0);
  270. RETRY_NEG(status, access(path, local_modes));
  271. if (status == 0)
  272. return s48_true_2(call);
  273. else
  274. switch (errno){
  275. case EACCES: /* access would be denied or search permission denied */
  276. case EROFS: /* want write access to a read-only filesystem */
  277. case ENOENT: /* no entry for a directory component */
  278. case ENOTDIR: /* using a non-directory as a directory */
  279. case ELOOP: /* too many symbolic links */
  280. return s48_false_2(call);
  281. default: /* all other errors are (supposed to be) real errors */
  282. s48_os_error_2(call, "posix_file_stuff/access", errno, 2, arg0, arg1); }
  283. }
  284. default:
  285. /* appease gcc -Wall */
  286. s48_assertion_violation_2(call, "posix_file_stuff", "invalid operation", 1, op);
  287. }
  288. return s48_unspecific_2(call);
  289. }
  290. /* ************************************************************ */
  291. /* File modes.
  292. *
  293. * We translate the local bits into our own bits and vice versa.
  294. */
  295. #define S48_ISUID 004000
  296. #define S48_ISGID 002000
  297. #define S48_ISVTX 001000 /* sticky bit, apparently not POSIX */
  298. #define S48_IRUSR 00400
  299. #define S48_IWUSR 00200
  300. #define S48_IXUSR 00100
  301. #define S48_IRGRP 00040
  302. #define S48_IWGRP 00020
  303. #define S48_IXGRP 00010
  304. #define S48_IROTH 00004
  305. #define S48_IWOTH 00002
  306. #define S48_IXOTH 00001
  307. s48_ref_t
  308. enter_mode(s48_call_t call, mode_t mode)
  309. {
  310. s48_ref_t sch_mode;
  311. mode_t my_mode;
  312. my_mode =
  313. (S_ISUID & mode ? S48_ISUID : 0) |
  314. (S_ISGID & mode ? S48_ISGID : 0) |
  315. (S_ISVTX & mode ? S48_ISVTX : 0) |
  316. (S_IRUSR & mode ? S48_IRUSR : 0) |
  317. (S_IWUSR & mode ? S48_IWUSR : 0) |
  318. (S_IXUSR & mode ? S48_IXUSR : 0) |
  319. (S_IRGRP & mode ? S48_IRGRP : 0) |
  320. (S_IWGRP & mode ? S48_IWGRP : 0) |
  321. (S_IXGRP & mode ? S48_IXGRP : 0) |
  322. (S_IROTH & mode ? S48_IROTH : 0) |
  323. (S_IWOTH & mode ? S48_IWOTH : 0) |
  324. (S_IXOTH & mode ? S48_IXOTH : 0);
  325. sch_mode = s48_make_record_2(call, posix_file_mode_type_binding);
  326. s48_unsafe_record_set_2(call, sch_mode, 0, s48_enter_long_2(call, my_mode));
  327. return sch_mode;
  328. }
  329. mode_t
  330. s48_extract_mode(s48_call_t call, s48_ref_t sch_mode)
  331. {
  332. mode_t c_mode;
  333. long mode;
  334. s48_check_record_type_2(call, sch_mode, posix_file_mode_type_binding);
  335. mode = s48_extract_long_2(call, s48_unsafe_record_ref_2(call, sch_mode, 0));
  336. c_mode =
  337. (S48_ISUID & mode ? S_ISUID : 0) |
  338. (S48_ISGID & mode ? S_ISGID : 0) |
  339. (S48_ISVTX & mode ? S_ISVTX : 0) |
  340. (S48_IRUSR & mode ? S_IRUSR : 0) |
  341. (S48_IWUSR & mode ? S_IWUSR : 0) |
  342. (S48_IXUSR & mode ? S_IXUSR : 0) |
  343. (S48_IRGRP & mode ? S_IRGRP : 0) |
  344. (S48_IWGRP & mode ? S_IWGRP : 0) |
  345. (S48_IXGRP & mode ? S_IXGRP : 0) |
  346. (S48_IROTH & mode ? S_IROTH : 0) |
  347. (S48_IWOTH & mode ? S_IWOTH : 0) |
  348. (S48_IXOTH & mode ? S_IXOTH : 0);
  349. return c_mode;
  350. }
  351. /* ************************************************************ */
  352. /*
  353. * Interface to stat(), fstat(), and lstat().
  354. */
  355. /* from time.c */
  356. extern s48_ref_t s48_posix_enter_time(s48_call_t call, time_t time);
  357. static s48_ref_t
  358. posix_file_info(s48_call_t call,
  359. s48_ref_t os_str_name,
  360. s48_ref_t svname,
  361. s48_ref_t follow_link_p,
  362. s48_ref_t mode_enum)
  363. {
  364. struct stat sbuf;
  365. int status;
  366. s48_ref_t scm_mode;
  367. s48_ref_t info;
  368. s48_ref_t temp;
  369. if (s48_channel_p_2(call, svname)) {
  370. RETRY_OR_RAISE_NEG(status,
  371. fstat(s48_unsafe_extract_long_2(call,
  372. s48_unsafe_channel_os_index_2(call, svname)),
  373. &sbuf)); }
  374. else if (s48_false_p_2(call, follow_link_p))
  375. RETRY_OR_RAISE_NEG(status, stat(s48_extract_byte_vector_readonly_2(call, svname), &sbuf));
  376. else
  377. RETRY_OR_RAISE_NEG(status, lstat(s48_extract_byte_vector_readonly_2(call, svname), &sbuf));
  378. info = s48_make_record_2(call, posix_file_info_type_binding);
  379. scm_mode = s48_vector_ref_2(call,
  380. mode_enum,
  381. S_ISREG(sbuf.st_mode) ? 0 :
  382. S_ISDIR(sbuf.st_mode) ? 1 :
  383. S_ISCHR(sbuf.st_mode) ? 2 :
  384. S_ISBLK(sbuf.st_mode) ? 3 :
  385. S_ISFIFO(sbuf.st_mode) ? 4 :
  386. /* next two are not POSIX */
  387. S_ISLNK(sbuf.st_mode) ? 5 :
  388. S_ISSOCK(sbuf.st_mode) ? 6 :
  389. 7);
  390. /* Stashing the various field values into temp before handing them
  391. off to S48_UNSAFE_RECORD_SET is necessary because their
  392. evaluation may cause GC; that GC could have destroyed the
  393. temporary holding the value of info before this function was
  394. moved to the new FFI. */
  395. s48_unsafe_record_set_2(call, info, 0, os_str_name);
  396. s48_unsafe_record_set_2(call, info, 1, scm_mode);
  397. temp = s48_enter_long_2(call, sbuf.st_dev);
  398. s48_unsafe_record_set_2(call, info, 2, temp);
  399. temp = s48_enter_long_2(call, sbuf.st_ino);
  400. s48_unsafe_record_set_2(call, info, 3, temp);
  401. temp = enter_mode(call, sbuf.st_mode);
  402. s48_unsafe_record_set_2(call, info, 4, temp);
  403. temp = s48_enter_long_2(call, sbuf.st_nlink);
  404. s48_unsafe_record_set_2(call, info, 5, temp);
  405. temp = s48_enter_uid(call, sbuf.st_uid);
  406. s48_unsafe_record_set_2(call, info, 6, temp);
  407. temp = s48_enter_gid(call, sbuf.st_gid);
  408. s48_unsafe_record_set_2(call, info, 7, temp);
  409. temp = s48_enter_long_2(call, sbuf.st_size);
  410. s48_unsafe_record_set_2(call, info, 8, temp);
  411. temp = s48_posix_enter_time(call, sbuf.st_atime);
  412. s48_unsafe_record_set_2(call, info, 9, temp);
  413. temp = s48_posix_enter_time(call, sbuf.st_mtime);
  414. s48_unsafe_record_set_2(call, info, 10, temp);
  415. temp = s48_posix_enter_time(call, sbuf.st_ctime);
  416. s48_unsafe_record_set_2(call, info, 11, temp);
  417. return info;
  418. }
  419. /* ************************************************************ */
  420. /*
  421. * Symbolic links.
  422. */
  423. s48_ref_t
  424. posix_create_symbolic_link(s48_call_t call,
  425. s48_ref_t svname1, s48_ref_t svname2)
  426. {
  427. int status;
  428. RETRY_OR_RAISE_NEG(status,
  429. symlink(s48_extract_byte_vector_readonly_2(call, svname1),
  430. s48_extract_byte_vector_readonly_2(call, svname2)));
  431. return s48_unspecific_2(call);
  432. }
  433. s48_ref_t
  434. posix_read_symbolic_link(s48_call_t call, s48_ref_t svname)
  435. {
  436. char local_buf[1024];
  437. char* buf = local_buf;
  438. ssize_t buf_size = 1024;
  439. ssize_t status;
  440. for (;;)
  441. {
  442. RETRY_NEG(status, readlink(s48_extract_byte_vector_readonly_2(call, svname), buf, buf_size - 1));
  443. if (status >= 0)
  444. {
  445. s48_ref_t result;
  446. buf[status] = '\0';
  447. result = s48_enter_byte_string_2(call, buf);
  448. if (buf != local_buf)
  449. free(buf);
  450. return result;
  451. }
  452. else if (status == ENAMETOOLONG)
  453. {
  454. if (buf != local_buf)
  455. free(buf);
  456. buf_size *= 2;
  457. buf = malloc(buf_size * sizeof(char));
  458. if (buf == NULL)
  459. s48_out_of_memory_error_2(call);
  460. }
  461. else
  462. s48_os_error_2(call, "posix_read_symbolic_link", errno, 1, svname);
  463. }
  464. }