You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

663 lines
13 KiB

  1. /* vim: set noet ts=8 sts=8 sw=8 : */
  2. /**
  3. * \file tree.c
  4. *
  5. * Implement filesystem trees.
  6. */
  7. #include <assert.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <time.h>
  11. #include <dirent.h>
  12. #include <libgen.h>
  13. #include <sys/types.h>
  14. #include <sys/stat.h>
  15. #include <unistd.h>
  16. #include <errno.h>
  17. #include <err.h>
  18. #include <stdio.h>
  19. #include "tree.h"
  20. #include "exclude.h"
  21. static void
  22. set_default_stat(struct stat *s)
  23. {
  24. time_t now = time(NULL);
  25. memset(s, 0, sizeof(struct stat));
  26. s->st_mode = 0555;
  27. s->st_atime = s->st_mtime = s->st_ctime = now;
  28. }
  29. void
  30. iso_tree_add_child(struct iso_tree_node_dir *parent,
  31. struct iso_tree_node *child)
  32. {
  33. assert(parent && child);
  34. assert(!child->parent);
  35. parent->nchildren++;
  36. parent->children =
  37. realloc(parent->children, parent->nchildren * sizeof(void*));
  38. parent->children[parent->nchildren-1] = child;
  39. child->parent = parent;
  40. }
  41. struct iso_tree_node_dir*
  42. iso_tree_new_root()
  43. {
  44. struct iso_tree_node_dir *root;
  45. root = calloc(1, sizeof(struct iso_tree_node_dir));
  46. set_default_stat(&root->node.attrib);
  47. root->node.refcount = 1;
  48. root->node.attrib.st_mode = S_IFDIR | 0777;
  49. root->node.type = LIBISO_NODE_DIR;
  50. return root;
  51. }
  52. struct iso_tree_node*
  53. iso_tree_add_file(struct iso_tree_node_dir *parent, const char *path)
  54. {
  55. struct iso_tree_node_file *f;
  56. char *p;
  57. struct stat st;
  58. assert( parent && path);
  59. if (lstat(path, &st) == -1) {
  60. libisofs_errno = NO_FILE;
  61. return NULL;
  62. }
  63. if ( !S_ISREG(st.st_mode) ) {
  64. libisofs_errno = UNEXPECTED_FILE_TYPE;
  65. return NULL;
  66. }
  67. if ( access(path, R_OK) ) {
  68. libisofs_errno = NO_READ_ACCESS;
  69. return NULL;
  70. }
  71. f = calloc(1, sizeof(struct iso_tree_node_file));
  72. /* fill fields */
  73. f->node.refcount = 1;
  74. f->node.attrib = st;
  75. f->loc.path = strdup(path);
  76. f->node.type = LIBISO_NODE_FILE;
  77. p = strdup(path); /* because basename() might modify its arg */
  78. f->node.name = strdup( basename(p) );
  79. free(p);
  80. /* add to parent (this also sets f->node->parent) */
  81. iso_tree_add_child(parent, (struct iso_tree_node*) f);
  82. return (struct iso_tree_node*) f;
  83. }
  84. struct iso_tree_node*
  85. iso_tree_add_symlink(struct iso_tree_node_dir *parent,
  86. const char *name, const char *dest)
  87. {
  88. struct iso_tree_node_symlink *link;
  89. assert( parent && name && dest);
  90. link = calloc(1, sizeof(struct iso_tree_node_symlink));
  91. /* fill fields */
  92. set_default_stat(&link->node.attrib);
  93. link->node.refcount = 1;
  94. link->node.attrib.st_mode |= S_IFLNK;
  95. link->node.name = strdup(name);
  96. link->node.type = LIBISO_NODE_SYMLINK;
  97. link->dest = strdup(dest);
  98. /* add to parent (this also sets link->node->parent) */
  99. iso_tree_add_child(parent, (struct iso_tree_node*) link);
  100. return (struct iso_tree_node*) link;
  101. }
  102. struct iso_tree_node_dir*
  103. iso_tree_add_dir(struct iso_tree_node_dir *parent,
  104. const char *name)
  105. {
  106. struct iso_tree_node_dir *dir;
  107. assert( parent && name );
  108. dir = calloc(1, sizeof(struct iso_tree_node_dir));
  109. dir->node.refcount = 1;
  110. dir->node.attrib = parent->node.attrib;
  111. dir->node.type = LIBISO_NODE_DIR;
  112. dir->node.name = strdup(name);
  113. iso_tree_add_child(parent, (struct iso_tree_node*) dir);
  114. return dir;
  115. }
  116. enum iso_tree_node_type
  117. iso_tree_node_get_type(struct iso_tree_node *node)
  118. {
  119. assert(node);
  120. return node->type;
  121. }
  122. void
  123. iso_tree_node_set_name(struct iso_tree_node *node, const char *name)
  124. {
  125. free(node->name);
  126. node->name = strdup(name);
  127. }
  128. const char *
  129. iso_tree_node_get_name(struct iso_tree_node *node)
  130. {
  131. assert(node);
  132. return node->name;
  133. }
  134. void
  135. iso_tree_node_set_hidden(struct iso_tree_node *node, int hide_attrs)
  136. {
  137. assert(node);
  138. node->hide_flags = hide_attrs;
  139. }
  140. int
  141. iso_tree_node_is_hidden(struct iso_tree_node *node)
  142. {
  143. assert(node);
  144. return node->hide_flags;
  145. }
  146. void
  147. iso_tree_node_set_gid(struct iso_tree_node *node, gid_t gid)
  148. {
  149. assert(node);
  150. node->attrib.st_gid = gid;
  151. }
  152. gid_t
  153. iso_tree_node_get_gid(struct iso_tree_node *node)
  154. {
  155. assert(node);
  156. return node->attrib.st_gid;
  157. }
  158. void
  159. iso_tree_node_set_uid(struct iso_tree_node *node, uid_t uid)
  160. {
  161. assert(node);
  162. node->attrib.st_uid = uid;
  163. }
  164. uid_t
  165. iso_tree_node_get_uid(struct iso_tree_node *node)
  166. {
  167. assert(node);
  168. return node->attrib.st_uid;
  169. }
  170. void
  171. iso_tree_node_set_permissions(struct iso_tree_node *node, mode_t mode)
  172. {
  173. assert(node);
  174. node->attrib.st_mode = (node->attrib.st_mode & S_IFMT) |
  175. (mode & ~S_IFMT);
  176. }
  177. mode_t
  178. iso_tree_node_get_permissions(struct iso_tree_node *node)
  179. {
  180. assert(node);
  181. return node->attrib.st_mode & ~S_IFMT;
  182. }
  183. off_t
  184. iso_tree_node_get_size(struct iso_tree_node *node)
  185. {
  186. return node->attrib.st_size;
  187. }
  188. void
  189. iso_tree_node_set_mtime(struct iso_tree_node *node, time_t time)
  190. {
  191. node->attrib.st_mtime = time;
  192. }
  193. time_t
  194. iso_tree_node_get_mtime(struct iso_tree_node *node)
  195. {
  196. return node->attrib.st_mtime;
  197. }
  198. void
  199. iso_tree_node_set_atime(struct iso_tree_node *node, time_t time)
  200. {
  201. node->attrib.st_atime = time;
  202. }
  203. time_t
  204. iso_tree_node_get_atime(struct iso_tree_node *node)
  205. {
  206. return node->attrib.st_atime;
  207. }
  208. void
  209. iso_tree_node_set_ctime(struct iso_tree_node *node, time_t time)
  210. {
  211. node->attrib.st_ctime = time;
  212. }
  213. time_t
  214. iso_tree_node_get_ctime(struct iso_tree_node *node)
  215. {
  216. return node->attrib.st_ctime;
  217. }
  218. void
  219. iso_tree_node_set_sort_weight(struct iso_tree_node *node, int w)
  220. {
  221. assert(node);
  222. if ( ISO_ISDIR(node) ) {
  223. size_t i;
  224. struct iso_tree_node_dir *dir;
  225. dir = (struct iso_tree_node_dir *) node;
  226. for (i=0; i < dir->nchildren; i++) {
  227. iso_tree_node_set_sort_weight(dir->children[i], w);
  228. }
  229. } else if ( ISO_ISREG(node) ) {
  230. struct iso_tree_node_file *file;
  231. file = (struct iso_tree_node_file *) node;
  232. file->sort_weight = w;
  233. }
  234. }
  235. void
  236. iso_tree_node_symlink_set_dest(struct iso_tree_node_symlink *node,
  237. const char *dest)
  238. {
  239. assert(node && dest);
  240. free(node->dest);
  241. node->dest = strdup(dest);
  242. }
  243. const char *
  244. iso_tree_node_symlink_get_dest(struct iso_tree_node_symlink *node)
  245. {
  246. assert(node);
  247. return node->dest;
  248. }
  249. struct iso_tree_node*
  250. iso_tree_add_node(struct iso_tree_node_dir *parent,
  251. const char *path)
  252. {
  253. struct stat st;
  254. struct iso_tree_node *node;
  255. assert( parent && path);
  256. if (lstat(path, &st) == -1) {
  257. libisofs_errno = NO_FILE;
  258. return NULL;
  259. }
  260. if ( access(path, R_OK) ) {
  261. libisofs_errno = NO_READ_ACCESS;
  262. return NULL;
  263. }
  264. switch (st.st_mode & S_IFMT) {
  265. case S_IFREG:
  266. /* regular file */
  267. node = iso_tree_add_file(parent, path);
  268. break;
  269. case S_IFLNK:
  270. /* symlink */
  271. {
  272. char dest[PATH_MAX];
  273. char *p;
  274. int n;
  275. n = readlink(path, dest, PATH_MAX);
  276. if ( n == -1 ) {
  277. libisofs_errno = INTERNAL_ERROR;
  278. return NULL;
  279. }
  280. dest[n] = '\0';
  281. p = strdup(path); /* because basename() might modify its arg */
  282. node = iso_tree_add_symlink(parent, basename(p), dest);
  283. free(p);
  284. node->attrib = st;
  285. }
  286. break;
  287. case S_IFDIR:
  288. /* directory */
  289. {
  290. char *p;
  291. p = strdup(path); /* because basename() might modify its arg */
  292. node = (struct iso_tree_node*) iso_tree_add_dir(parent, basename(p));
  293. free(p);
  294. node->attrib = st;
  295. }
  296. break;
  297. default:
  298. libisofs_errno = UNEXPECTED_FILE_TYPE;
  299. node = NULL;
  300. break;
  301. }
  302. return node;
  303. }
  304. struct iso_tree_iter *
  305. iso_tree_node_children(struct iso_tree_node_dir *dir)
  306. {
  307. struct iso_tree_iter *iter;
  308. assert(dir);
  309. iter = malloc(sizeof(struct iso_tree_iter));
  310. iter->dir = dir;
  311. iter->index = -1;
  312. return iter;
  313. }
  314. struct iso_tree_node *
  315. iso_tree_iter_next(struct iso_tree_iter *iter)
  316. {
  317. assert(iter);
  318. if ( ++iter->index < iter->dir->nchildren )
  319. return iter->dir->children[iter->index];
  320. else
  321. return NULL;
  322. }
  323. int
  324. iso_tree_iter_has_next(struct iso_tree_iter *iter)
  325. {
  326. assert(iter);
  327. return iter->index + 1 < iter->dir->nchildren;
  328. }
  329. void
  330. iso_tree_iter_free(struct iso_tree_iter *iter)
  331. {
  332. free(iter);
  333. }
  334. int
  335. iso_tree_node_take(struct iso_tree_node_dir *dir, struct iso_tree_node *node)
  336. {
  337. int i;
  338. assert(dir && node);
  339. /* search for the node in the dir */
  340. for (i = 0; i < dir->nchildren; ++i) {
  341. if ( dir->children[i] == node )
  342. break;
  343. }
  344. if (i < dir->nchildren) {
  345. int j;
  346. for (j = i+1; j < dir->nchildren; ++j) {
  347. dir->children[j-1] = dir->children[j];
  348. }
  349. --dir->nchildren;
  350. dir->children = realloc(dir->children, dir->nchildren * sizeof(void*));
  351. node->parent = NULL;
  352. return 0;
  353. } else {
  354. /* the node doesn't exist on dir */
  355. return -1;
  356. }
  357. }
  358. int
  359. iso_tree_node_remove(struct iso_tree_node_dir *dir, struct iso_tree_node *node)
  360. {
  361. int res;
  362. assert(dir && node);
  363. res = iso_tree_node_take(dir, node);
  364. if (!res)
  365. iso_tree_free(node);
  366. return res;
  367. }
  368. int
  369. iso_tree_node_take_iter(struct iso_tree_iter *iter)
  370. {
  371. int j;
  372. struct iso_tree_node_dir *dir;
  373. struct iso_tree_node *node;
  374. assert(iter);
  375. dir = iter->dir;
  376. if (iter->index < 0)
  377. return -1; /* index before beginning */
  378. if (iter->index >= dir->nchildren)
  379. return -2; /* index after end */
  380. node = dir->children[iter->index];
  381. node->parent = NULL;
  382. for (j = iter->index+1; j < dir->nchildren; ++j) {
  383. dir->children[j-1] = dir->children[j];
  384. }
  385. --dir->nchildren;
  386. dir->children = realloc(dir->children, dir->nchildren * sizeof(void*));
  387. /* update iter index */
  388. --iter->index;
  389. return 0;
  390. }
  391. int
  392. iso_tree_node_remove_iter(struct iso_tree_iter *iter)
  393. {
  394. int j;
  395. struct iso_tree_node_dir *dir;
  396. struct iso_tree_node *node;
  397. assert(iter);
  398. dir = iter->dir;
  399. if (iter->index < 0)
  400. return -1; /* index before beginning */
  401. if (iter->index >= dir->nchildren)
  402. return -2; /* index after end */
  403. node = dir->children[iter->index];
  404. for (j = iter->index+1; j < dir->nchildren; ++j) {
  405. dir->children[j-1] = dir->children[j];
  406. }
  407. --dir->nchildren;
  408. dir->children = realloc(dir->children, dir->nchildren * sizeof(void*));
  409. /* update iter index */
  410. --iter->index;
  411. /* and free node */
  412. node->parent = NULL;
  413. iso_tree_free(node);
  414. return 0;
  415. }
  416. struct iso_tree_node_dir *
  417. iso_tree_node_get_parent(struct iso_tree_node *node)
  418. {
  419. assert(node);
  420. return node->parent;
  421. }
  422. void
  423. iso_tree_node_ref(struct iso_tree_node *node)
  424. {
  425. ++node->refcount;
  426. }
  427. void
  428. iso_tree_free(struct iso_tree_node *root)
  429. {
  430. if (!root)
  431. return;
  432. if (--root->refcount < 1) {
  433. if ( ISO_ISDIR(root) ) {
  434. size_t i;
  435. struct iso_tree_node_dir *dir;
  436. dir = (struct iso_tree_node_dir *) root;
  437. for (i=0; i < dir->nchildren; i++) {
  438. iso_tree_free(dir->children[i]);
  439. }
  440. free(dir->children);
  441. } else if ( ISO_ISLNK(root) ) {
  442. struct iso_tree_node_symlink *link;
  443. link = (struct iso_tree_node_symlink *) root;
  444. free(link->dest);
  445. } else if ( ISO_ISREG(root) ) {
  446. struct iso_tree_node_file *file;
  447. file = (struct iso_tree_node_file *) root;
  448. if (root->procedence == LIBISO_NEW)
  449. free(file->loc.path);
  450. } else if ( ISO_ISBOOT(root) ) {
  451. struct iso_tree_node_boot *boot;
  452. boot = (struct iso_tree_node_boot *) root;
  453. if (root->procedence == LIBISO_NEW && boot->img)
  454. free(boot->loc.path);
  455. }
  456. free(root->name);
  457. free(root);
  458. }
  459. }
  460. static void
  461. iso_tree_radd_dir_aux(struct iso_tree_node_dir *parent, const char *path,
  462. struct iso_tree_radd_dir_behavior *behavior,
  463. struct iso_hash_table *excludes)
  464. {
  465. struct iso_tree_node *new;
  466. DIR *dir;
  467. struct dirent *ent;
  468. dir = opendir(path);
  469. if (!dir) {
  470. warn("couldn't open directory %s: %s\n", path, strerror(errno));
  471. return;
  472. }
  473. while ((ent = readdir(dir))) {
  474. char child[strlen(ent->d_name) + strlen(path) + 2];
  475. if (behavior->stop_on_error & behavior->error)
  476. break;
  477. if (strcmp(ent->d_name, ".") == 0 ||
  478. strcmp(ent->d_name, "..") == 0)
  479. continue;
  480. //TODO check if path already finished in '/'
  481. sprintf(child, "%s/%s", path, ent->d_name);
  482. /* see if this child is excluded. */
  483. if (iso_exclude_lookup(excludes, child))
  484. continue;
  485. new = iso_tree_add_node(parent, child);
  486. if (!new || !ISO_ISDIR(new)) {
  487. if (!new)
  488. behavior->error = 1;
  489. continue;
  490. }
  491. iso_tree_radd_dir_aux( (struct iso_tree_node_dir *) new, child,
  492. behavior, excludes);
  493. }
  494. closedir(dir);
  495. return;
  496. }
  497. void
  498. iso_tree_radd_dir(struct iso_tree_node_dir *parent, const char *path,
  499. struct iso_tree_radd_dir_behavior *behavior)
  500. {
  501. struct iso_tree_node_dir *dir;
  502. struct iso_hash_table table = { {0,}, 0};
  503. assert ( parent && path );
  504. behavior->error = 0;
  505. /* initialize exclude hash_table */
  506. if ( behavior->excludes ) {
  507. char *exclude;
  508. int i = 0;
  509. while ( (exclude = behavior->excludes[i++]) ) {
  510. iso_exclude_add_path(&table, exclude);
  511. }
  512. }
  513. /* recurse into dir */
  514. iso_tree_radd_dir_aux(parent, path, behavior, &table);
  515. /* clear hashtable */
  516. iso_exclude_empty(&table);
  517. }
  518. void
  519. iso_tree_print(const struct iso_tree_node *root, int spaces)
  520. {
  521. char sp[spaces+1];
  522. memset(sp, ' ', spaces);
  523. sp[spaces] = '\0';
  524. printf("%s%s\n", sp, root->name);
  525. if ( ISO_ISDIR(root) ) {
  526. size_t i;
  527. struct iso_tree_node_dir *dir;
  528. dir = (struct iso_tree_node_dir *) root;
  529. for (i=0; i < dir->nchildren; i++) {
  530. iso_tree_print(dir->children[i], spaces+2);
  531. }
  532. }
  533. }
  534. void
  535. iso_tree_print_verbose(const struct iso_tree_node *root,
  536. print_dir_callback dir,
  537. print_file_callback file,
  538. void *callback_data,
  539. int spaces)
  540. {
  541. (ISO_ISDIR(root) ? dir : file)
  542. (root, callback_data, spaces);
  543. if ( ISO_ISDIR(root) ) {
  544. size_t i;
  545. struct iso_tree_node_dir *dir_node;
  546. dir_node = (struct iso_tree_node_dir *) root;
  547. for (i=0; i < dir_node->nchildren; i++) {
  548. iso_tree_print_verbose(dir_node->children[i], dir,
  549. file, callback_data, spaces+2);
  550. }
  551. }
  552. }