dir.c 14 KB

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