Expunge revision histories in files.
[sw-tools] / src / sw_links.c
CommitLineData
3315e8b3 1/* -*-c-*-
2 *
9796a787 3 * $Id: sw_links.c,v 1.2 2004/04/08 01:52:19 mdw Exp $
3315e8b3 4 *
5 * Messing with symlink trees
6 *
7 * (c) 1999 EBI
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of sw-tools.
13 *
14 * sw-tools is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * sw-tools is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with sw-tools; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
3315e8b3 29/*----- Header files ------------------------------------------------------*/
30
31#include <errno.h>
32#include <limits.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <time.h>
37
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <dirent.h>
41#include <unistd.h>
42#include <utime.h>
43#include <fcntl.h>
44
45#include <mLib/alloc.h>
46#include <mLib/dspool.h>
47#include <mLib/dstr.h>
48#include <mLib/report.h>
49#include <mLib/sub.h>
50
51#include "sw_arch.h"
52#include "sw_build.h"
53#include "sw_links.h"
54
55/*----- Data structures ---------------------------------------------------*/
56
57typedef struct fent {
58 struct fent *next;
59 dstr *name;
60 dstr *link;
61 struct stat st;
62} fent;
63
64/*----- Static variables --------------------------------------------------*/
65
66static dstr down;
67static dstr up;
68static archcons *alist;
69static archcons *all;
70static dspool pool;
71
72/*----- Main code ---------------------------------------------------------*/
73
74/* --- @canon@ --- *
75 *
76 * Arguments: @dstr *d@ = path string to canonify
77 *
78 * Returns: ---
79 *
80 * Use: Strips out all `dir/..' pairs in a path string. Also removes
81 * any `.' and empty components.
82 */
83
84static void canon(dstr *d)
85{
86#define MAXBUF 16
87 char *b[MAXBUF];
88 int bp;
89 int v, n;
90 int retry;
91 char *path;
92 char *p, *q;
93 int r;
94
95 /* --- Initial stuff --- *
96 *
97 * If @path@ starts with `/' then initial `..' and `.' sequences can be
98 * discarded immediately. Remember this by remembering where the start of
99 * the string is.
100 */
101
102 DPUTZ(d);
103 p = q = path = d->buf;
104 if (*p == '/') {
105 p++; q++; path++;
106 r = 1;
107 } else
108 r = 0;
109
110 /* --- Now for the main job --- *
111 *
112 * Scan each path component. If it's a normal name, store @q@ in my
113 * circular buffer, and copy its text from @p@ to @q@. If it's blank,
114 * or `.' then skip @p@ past it. If it's `..', and there's an entry in my
115 * buffer, then reset @q@ back to that position and skip @p@ on past;
116 * otherwise, copy it to @q@.
117 *
118 * Complications arise when the buffer gets full. Old entries are
119 * discarded off the bottom. If it turns out they were useful (because
120 * @n@ is zero, but @v@ isn't) then @retry@ is set and we go round again
121 * when we're finished.
122 */
123
124again:
125 bp = n = v = 0;
126 retry = 0;
127
128 while (*p) {
129
130 /* --- Skip empty items --- */
131
132 while (*p == '/')
133 p++;
134
135 /* --- Things with `.' in front of them --- */
136
137 if (*p == '.') {
138 if (p[1] == 0)
139 break;
140 else if (p[1] == '/') {
141 p += 2;
142 continue;
143 } else if (p[1] == '.' && (p[2] == '/' || p[2] == 0)) {
144 if (n) {
145 bp = bp ? bp - 1 : MAXBUF - 1;
146 q = b[bp];
147 n--; v--;
148 p += 2;
149 continue;
150 } else {
151 if (v)
152 retry = 1;
153 else if (r) {
154 p += 2;
155 continue;
156 }
157 goto out;
158 }
159 }
160 }
161
162 /* --- Normal things --- */
163
164 b[bp++] = q;
165 if (bp >= MAXBUF) bp = 0;
166 v++;
167 if (n < MAXBUF) n++;
168 out:
169 while (*p && *p != '/')
170 *q++ = *p++;
171 if (*p == '/')
172 *q++ = *p++;
173 }
174 *q = 0;
175
176 /* --- Tidying up --- */
177
178 if (retry) {
179 p = q = path;
180 goto again;
181 }
182 if (q > path && q[-1] == '/')
183 q[-1] = 0;
184 d->len = q - d->buf;
185}
186
187/* --- @linktree@ --- *
188 *
189 * Arguments: @int top@ = nonzero if this is toplevel
190 * @dstr *cont@ = continuation directory
191 *
192 * Returns: Zero if things are working well, nonzero otherwise.
193 *
194 * Use: Main recursive tree-linking algorithm. This makes extensive
195 * use of the dynamic strings @up@ and @down@ as stacks to
196 * maintain state.
197 *
198 * On entry, @down@ contains the name of the path to scan,
199 * relative to the common root; @up@ is the inverse path, from
200 * the directory @down@ back up to the root. As a special
201 * wrinkle, @up@ has an additional `../' on the front. The
202 * current directory is @down@. On exit, the current directory
203 * is set to @cont@.
204 *
205 * See the `description of the algorithm' below to really
206 * understand what's going on here. It's not completely
207 * trivial.
208 */
209
210static int linktree(int top, dstr *cont)
211{
212 int rc = 0;
213 fent *fs;
214 dstr *dd;
215
216 if (!alist)
217 return (0);
218
219 DSGET(&pool, dd);
220
221 /* --- Description of the algorithm ---
222 *
223 * This is the sort of thing which is easy to do badly and hard to do
224 * well. The current algorithm seeks to minimize the amount of searching
225 * that the operating system has to do around the filesystem, by changing
226 * the current directory fairly often. But it also tries to avoid using
227 * too much memory, and never visits the same directory twice.
228 *
229 * I start off in the `common root'. This is the directory that the user
230 * actually wanted to make link trees of, and is the parent of all the link
231 * trees. The algorithm keeps track of where it's meant to be using two
232 * (static) variables, @down@ and @up@. The @down@ variable tracks the
233 * current directory relative to the common root, and the @up@ variable
234 * contains enough `../'s to get back to the common root from the current
235 * directory, and one more (for getting back into the main tree from one of
236 * the architecture link trees, which has an extra level of depth). When a
237 * recursion stage is entered, the current directory is already set
238 * correctly.
239 *
240 * At any stage in the recursion, there is a `continuation' directory.
241 * This is where my caller wants you to go when I've finished my job. If I
242 * have no subdirectories to cope with, then I just change into the
243 * continuation when I've finished making all my links. The trick with
244 * continations really works with subdirectories. I change into the first
245 * subdirectory in my list myself, passing it a continuation for the next
246 * subdirectory in sequence. The last subdirectory gets given a modified
247 * version of my own contination which keeps my caller happy without me
248 * actually having to do anything.
249 *
250 * The actual work is done by a postorder traversal. A directory is
251 * processed in four phases, with an interlude between phases three and
252 * four.
253 *
254 * * Phase one scans the current directory, and stores the names of
255 * everything in a list. Uninteresting things like `.' and `..', and
256 * the architecture trees at toplevel, are filtered out at this stage.
257 * When this scan is complete, I no longer need a file descriptor open
258 * on the directory, and I can close it.
259 *
260 * * Phase two examines each item scanned in phase one, and determines
261 * how to deal with it. Directory objects turn into real hard
262 * directories; symlinks turn into adjusted symlinks to the same
263 * destination; and files turn into relative symlinks of the right
264 * kind.
265 *
266 * * Phase three moves into the corresponding link directory for each
267 * architecture, and makes the links and directories decided upon in
268 * phase two. When phase three is complete, the current directory is
269 * still the link directory for the last architecture.
270 *
271 * * The interlude tidies up some internal structures a little, and
272 * handles early exit. Firstly, the list of objects is filtered, and
273 * everything which isn't a directory is removed. Secondly, if the
274 * list is now empty, the algorithm does its `early exit': it works out
275 * how to get to the continuation directory from where it is now, does
276 * that, and then returns.
277 *
278 * * Phase four does the recursion step. There is at least one
279 * subdirectory to deal with. Change out of the link tree, and into
280 * the first subdirectory of my main directory. Now, for each
281 * subdirectory, recursively build trees, setting @down@, @up@ and the
282 * continuation according to the description above.
283 *
284 * That completes the algorithm.
285 */
286
287 {
288 /* --- Phase one: directory scan --- */
289
290 DIR *dp;
291 struct dirent *d;
292 fent **ff, *f;
293 dstr *ds;
294
295 /* --- Open a directory stream --- */
296
297 if ((dp = opendir(".")) == 0) {
298 moan("couldn't read directory `%s': %s", down.buf, strerror(errno));
299 chdir(cont->buf);
300 return (-1);
301 }
302
303 /* --- Read the entries out one by one --- */
304
305 ff = &fs;
306 while ((d = readdir(dp)) != 0) {
307
308 /* --- Skip `.' and `..' directories --- */
309
310 {
311 char *p = d->d_name;
312 if (p[0] == '.' && ((p[1] == '.' && p[2] == 0) || p[1] == 0))
313 goto skip;
314 }
315
316 /* --- If this is toplevel, skip over the symlink trees --- */
317
318 if (top) {
319 archcons *a;
320 for (a = all; a; a = a->cdr) {
321 if (strcmp(d->d_name, a->car->arch) == 0)
322 goto skip;
323 }
324 }
325
326 /* --- Make a little structure with the entries in --- */
327
328 DSGET(&pool, ds);
329 DPUTS(ds, d->d_name);
330 f = CREATE(fent);
331 f->name = ds;
332 *ff = f;
333 ff = &f->next;
334 skip:;
335 }
336
337 closedir(dp);
338 *ff = 0;
339 }
340
341 {
342 /* -- Phase two: read attributes --- */
343
344 fent *f;
345
346 for (f = fs; f; f = f->next) {
347
348 /* --- Read the file information --- */
349
350 if (lstat(f->name->buf, &f->st)) {
351 moan("couldn't stat file `%s%s': %s",
352 down.buf, f->name->buf, strerror(errno));
353 DSPUT(&pool, f->name);
354 f->name = 0;
355 rc = -1;
356 }
357
358 /* --- Handle symbolic links --- *
359 *
360 * I need to canonify relative symbolic links. (And there shouldn't be
361 * any absolute links in a source distribution!)
362 */
363
364 else if (S_ISLNK(f->st.st_mode)) {
365 dstr *ds;
366 int i;
367 DSGET(&pool, ds);
368 DENSURE(ds, f->st.st_size + 1);
369 if ((i = readlink(f->name->buf, ds->buf, ds->sz)) < 0) {
370 moan("couldn't read symbolic link `%s%s': %s",
371 down.buf, f->name->buf, strerror(errno));
372 } else {
373 ds->buf[i] = 0;
374 ds->len = i;
375 if (ds->buf[0] == '/')
376 f->link = ds;
377 else {
378 dstr *d;
379 DSGET(&pool, d);
380 f->link = d;
381 DPUTD(d, &up);
382 DPUTD(d, &down);
383 DPUTD(d, ds);
384 canon(d);
385 DSPUT(&pool, ds);
386 }
387 }
388 }
389
390 /* --- Directories are easy: they get created the hard way --- */
391
392 else if (S_ISDIR(f->st.st_mode))
393 f->link = 0;
394
395 /* --- Everything else is just a link --- */
396
397 else {
398 dstr *d;
399 DSGET(&pool, d);
400 f->link = d;
401 DPUTD(d, &up);
402 DPUTD(d, &down);
403 DPUTD(d, f->name);
404 }
405 }
406 }
407
408 {
409 /* --- Phase three: output links --- */
410
411 archcons *a;
412
413 /* --- Step 1: change directory --- */
414
415 dd->len = 0;
416 DPUTD(dd, &up);
417 DPUTS(dd, alist->car->arch);
418 DPUTC(dd, '/');
419 DPUTD(dd, &down);
420 if (chdir(dd->buf + 3)) {
421 die(1, "fatal error: couldn't change directory to `%s': %s",
422 dd->buf + 3, strerror(errno));
423 }
424
425 a = alist;
426 for (;;) {
427
428 /* --- Step 2: populate with links --- */
429
430 archent *e = a->car;
431 fent *f;
432
433 for (f = fs; f; f = f->next) {
434 if (f->link) {
435 {
436 struct stat st;
437 if (lstat(f->name->buf, &st) == 0 && S_ISLNK(st.st_mode))
438 unlink(f->name->buf);
439 }
440 if (symlink(f->link->buf, f->name->buf)) {
441 moan("couldn't create link `%s%s/%s': %s",
442 e->arch, down.buf, f->name->buf, strerror(errno));
443 rc = -1;
444 }
445 } else if (f->name) {
446 if (mkdir(f->name->buf, f->st.st_mode & 07777) &&
447 errno != EEXIST) {
448 moan("couldn't create directory `%s%s/%s: %s",
449 e->arch, down.buf, f->name->buf, strerror(errno));
450 rc = -1;
451 }
452 }
453 }
454
455 /* --- Step 3: move along --- */
456
457 a = a->cdr;
458 if (!a)
459 break;
460
461 dd->len = 0;
462 DPUTD(dd, &up);
463 DPUTS(dd, a->car->arch);
464 DPUTC(dd, '/');
465 DPUTD(dd, &down);
466 if (chdir(dd->buf)) {
467 die(1, "fatal error: couldn't change directory to `%s': %s",
468 dd->buf, strerror(errno));
469 }
470 }
471 }
472
473 /* --- Interlude: filter out nondirectories from the file list --- *
474 *
475 * This is a memory-saving exercise, and it makes the subdirectory handling
476 * simpler.
477 */
478
479 {
480 fent **ff = &fs;
481 while (*ff) {
482 fent *f = *ff;
483 if (f->name && !f->link)
484 ff = &f->next;
485 else {
486 if (f->name)
487 DSPUT(&pool, f->name);
488 if (f->link)
489 DSPUT(&pool, f->link);
490 *ff = f->next;
491 DESTROY(f);
492 }
493 }
494 }
495
496 /* --- Interlude: early exit if no directories --- *
497 *
498 * Presumably, a call to @canon@ is cheaper than traversing too many
499 * directories in the kernel.
500 */
501
502 if (!fs) {
503 dd->len = 0;
504 DPUTD(dd, &up);
505 DPUTD(dd, &down);
506 DPUTD(dd, cont);
507 canon(dd);
508 if (chdir(dd->buf)) {
509 die(1, "fatal error: couldn't change directory to `%s': %s",
510 dd->buf, strerror(errno));
511 }
512 DSPUT(&pool, dd);
513 return (rc);
514 }
515
516 {
517 /* --- Phase four: process subdirectories --- */
518
519 fent *f;
520 size_t ulen = up.len, dlen = down.len;
521
522 /* --- Set current directory for first directory --- *
523 *
524 * Subsequent directories do the right thing with the @cont@ argument.
525 * Then just leave this one queued up for the next time around.
526 */
527
528 dd->len = 0;
529 DPUTD(dd, &up);
530 DPUTD(dd, &down);
531 DPUTD(dd, fs->name);
532 if (chdir(dd->buf)) {
533 die(1, "fatal error: couldn't change directory to `%s': %s",
534 dd->buf, strerror(errno));
535 }
536
537 /* --- Now just process all the directories in turn --- */
538
539 f = fs;
540 while (f) {
541
542 /* --- Sort out the new `up' and `down' --- */
543
544 up.len = ulen;
545 down.len = dlen;
546 DPUTS(&up, "../");
547 DPUTD(&down, f->name);
548 DPUTS(&down, "/");
549
550 /* --- Set up the continuation directory --- */
551
552 dd->len = 0;
553 DPUTS(dd, "../");
554 if (f->next)
555 DPUTD(dd, f->next->name);
556 else
557 DPUTD(dd, cont);
558
559 /* --- Clean up this node --- */
560
561 {
562 fent *fnext = f->next;
563 DSPUT(&pool, f->name);
564 DESTROY(f);
565 f = fnext;
566 }
567
568 /* --- Go for it --- */
569
570 linktree(0, dd);
571 }
572 }
573
574 DSPUT(&pool, dd);
575 return (rc);
576}
577
578/* --- @snap@ --- *
579 *
580 * Arguments: @const char *f@ = filename to snap
581 *
582 * Returns: Zero if ok, nonzero otherwise.
583 *
584 * Use: Snaps a symlink in one of the symlink trees into a real file.
585 * Also (by design) happens to work even if there wasn't a
586 * symlink there to begin with, in which case any necessary
587 * directories are created beforehand.
588 */
589
590static int snap(const char *f)
591{
592 int narch;
593 int *fd;
594 int ifd;
595 struct stat st;
596 dstr *d;
597 int rc = 0;
598
599 /* --- Open the input file --- */
600
601 if ((ifd = open(f, O_RDONLY)) < 0) {
602 moan("couldn't open `%s' for reading: %s", f, strerror(errno));
603 return (-1);
604 }
605
606 if (fstat(ifd, &st)) {
607 moan("couldn't read information about `%s': %s", f, strerror(errno));
608 return (-1);
609 }
610
611 /* --- Count the architectures --- */
612
613 { archcons *a; for (a = alist, narch = 0; a; a = a->cdr, narch++) ; }
614 d = xmalloc(narch * sizeof(dstr));
615 fd = xmalloc(narch * sizeof(int));
616
617 /* --- Make the directories needed, remove the old files, and so on --- */
618
619 {
620 int i;
621 archcons *a;
622 char *p = xstrdup(f);
623 char *q;
624
625 for (i = 0; i < narch; i++)
626 DCREATE(&d[i]);
627 for (i = 0, a = alist; a; i++, a = a->cdr)
628 DPUTS(&d[i], a->car->arch);
629 for (q = strtok(p, "/"); q; q = strtok(0, "/")) {
630 for (i = 0; i < narch; i++) {
631 mkdir(d[i].buf, 0775);
632 DPUTC(&d[i], '/');
633 DPUTS(&d[i], q);
634 }
635 }
636 for (i = 0; i < narch; i++) {
637 unlink(d[i].buf);
638 if ((fd[i] = open(d[i].buf, O_WRONLY | O_TRUNC | O_CREAT,
639 st.st_mode & 07777)) < 0) {
640 moan("couldn't open `%s' for writing: %s",
641 d[i].buf, strerror(errno));
642 rc = -1;
643 }
644 }
645 free(p);
646 }
647
648 /* --- Main data copy loop --- */
649
650 {
651 int i;
652 char buf[BUFSIZ];
653 ssize_t n;
654
655 for (;;) {
656 n = read(ifd, buf, sizeof(buf));
657 if (n < 0) {
658 moan("error reading `%s': %s", f, strerror(errno));
659 rc = -1;
660 for (i = 0; i < narch; i++) {
661 close(fd[i]);
662 fd[i] = -1;
663 unlink(d[i].buf);
664 }
665 break;
666 }
667 if (!n)
668 break;
669 for (i = 0; i < narch; i++) {
670 if (fd[i] < 0)
671 continue;
672 if (write(fd[i], buf, n) < 0) {
673 moan("error writing `%s', %s", d[i].buf, strerror(errno));
674 close(fd[i]);
675 fd[i] = -1;
676 unlink(d[i].buf);
677 rc = -1;
678 }
679 }
680 }
681 }
682
683 /* --- Set the state on the finished files --- */
684
685 {
686 int i;
687 struct utimbuf u;
688
689 u.actime = st.st_atime;
690 u.modtime = st.st_mtime;
691
692 for (i = 0; i < narch; i++) {
693 if (fd[i] >= 0) {
694 close(fd[i]);
695 chmod(d[i].buf, st.st_mode & 07777);
696 utime(d[i].buf, &u);
697 }
698 DDESTROY(&d[i]);
699 }
700 }
701
702 free(d);
703 free(fd);
704 return (rc);
705}
706
707/*----- Subcommands -------------------------------------------------------*/
708
709/* --- @sw_link@ --- */
710
711int sw_link(int argc, char *argv[])
712{
713 int rc = 0;
714 swinfo sw;
715
716 if (argc != 1)
717 die(1, "Usage: linktree");
718
719 /* --- Initialize the dynamic strings --- */
720
721 dstr_create(&up);
722 dstr_puts(&up, "../");
723 dstr_create(&down);
724 dstr_putz(&down);
725 dspool_create(&pool, 32);
726
727 /* --- Set up the architecture lists --- */
728
729 if (swinfo_fetch(&sw)) {
730 die(1, "couldn't read build status: %s (try running setup)",
731 strerror(errno));
732 }
733 swinfo_sanity(&sw);
734 all = arch_readtab();
735 alist = swbuild_archlist(&sw);
736
737 if (!alist) {
738 moan("All desired architectures already built!");
739 return (0);
740 }
741
742 {
743 archcons *a;
744 for (a = alist; a; a = a->cdr) {
745 if (mkdir(a->car->arch, 0775) && errno != EEXIST) {
746 moan("couldn't create architecture tree `%s': %s",
747 a->car->arch, strerror(errno));
748 rc = -1;
749 }
750 }
751 }
752
753 /* --- Go --- */
754
755 if (rc == 0) {
756 dstr d = DSTR_INIT;
757 rc = linktree(1, &d);
758 DDESTROY(&d);
759 }
760
761 /* --- Clean up the mess --- */
762
763 dspool_destroy(&pool);
764 return (!!rc);
765}
766
767/* --- @sw_snap@ --- */
768
769int sw_snap(int argc, char *argv[])
770{
771 int rc = 0;
772 swinfo sw;
773 int i;
774
775 if (argc < 2)
776 die(1, "Usage: snaplink FILE...");
777
778 /* --- Set up the architecture lists --- */
779
780 if (swinfo_fetch(&sw)) {
781 die(1, "couldn't read build status: %s (try running setup)",
782 strerror(errno));
783 }
784 swinfo_sanity(&sw);
785 alist = swbuild_archlist(&sw);
786
787 if (!alist) {
788 moan("All desired architectures already built!");
789 return (0);
790 }
791
792 for (i = 1; i < argc; i++) {
793 if (snap(argv[i]))
794 rc = 1;
795 }
796
797 return (rc);
798}
799
800/*----- That's all, folks -------------------------------------------------*/