3 * $Id: sw_links.c,v 1.2 2004/04/08 01:52:19 mdw Exp $
5 * Messing with symlink trees
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of sw-tools.
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.
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.
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.
29 /*----- Header files ------------------------------------------------------*/
38 #include <sys/types.h>
45 #include <mLib/alloc.h>
46 #include <mLib/dspool.h>
47 #include <mLib/dstr.h>
48 #include <mLib/report.h>
55 /*----- Data structures ---------------------------------------------------*/
64 /*----- Static variables --------------------------------------------------*/
68 static archcons *alist;
72 /*----- Main code ---------------------------------------------------------*/
76 * Arguments: @dstr *d@ = path string to canonify
80 * Use: Strips out all `dir/..' pairs in a path string. Also removes
81 * any `.' and empty components.
84 static void canon(dstr *d)
95 /* --- Initial stuff --- *
97 * If @path@ starts with `/' then initial `..' and `.' sequences can be
98 * discarded immediately. Remember this by remembering where the start of
103 p = q = path = d->buf;
110 /* --- Now for the main job --- *
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@.
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.
130 /* --- Skip empty items --- */
135 /* --- Things with `.' in front of them --- */
140 else if (p[1] == '/') {
143 } else if (p[1] == '.' && (p[2] == '/' || p[2] == 0)) {
145 bp = bp ? bp - 1 : MAXBUF - 1;
162 /* --- Normal things --- */
165 if (bp >= MAXBUF) bp = 0;
169 while (*p && *p != '/')
176 /* --- Tidying up --- */
182 if (q > path && q[-1] == '/')
187 /* --- @linktree@ --- *
189 * Arguments: @int top@ = nonzero if this is toplevel
190 * @dstr *cont@ = continuation directory
192 * Returns: Zero if things are working well, nonzero otherwise.
194 * Use: Main recursive tree-linking algorithm. This makes extensive
195 * use of the dynamic strings @up@ and @down@ as stacks to
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
205 * See the `description of the algorithm' below to really
206 * understand what's going on here. It's not completely
210 static int linktree(int top, dstr *cont)
221 /* --- Description of the algorithm ---
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.
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
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.
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
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.
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
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.
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.
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.
284 * That completes the algorithm.
288 /* --- Phase one: directory scan --- */
295 /* --- Open a directory stream --- */
297 if ((dp = opendir(".")) == 0) {
298 moan("couldn't read directory `%s': %s", down.buf, strerror(errno));
303 /* --- Read the entries out one by one --- */
306 while ((d = readdir(dp)) != 0) {
308 /* --- Skip `.' and `..' directories --- */
312 if (p[0] == '.' && ((p[1] == '.' && p[2] == 0) || p[1] == 0))
316 /* --- If this is toplevel, skip over the symlink trees --- */
320 for (a = all; a; a = a->cdr) {
321 if (strcmp(d->d_name, a->car->arch) == 0)
326 /* --- Make a little structure with the entries in --- */
329 DPUTS(ds, d->d_name);
342 /* -- Phase two: read attributes --- */
346 for (f = fs; f; f = f->next) {
348 /* --- Read the file information --- */
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);
358 /* --- Handle symbolic links --- *
360 * I need to canonify relative symbolic links. (And there shouldn't be
361 * any absolute links in a source distribution!)
364 else if (S_ISLNK(f->st.st_mode)) {
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));
375 if (ds->buf[0] == '/')
390 /* --- Directories are easy: they get created the hard way --- */
392 else if (S_ISDIR(f->st.st_mode))
395 /* --- Everything else is just a link --- */
409 /* --- Phase three: output links --- */
413 /* --- Step 1: change directory --- */
417 DPUTS(dd, alist->car->arch);
420 if (chdir(dd->buf + 3)) {
421 die(1, "fatal error: couldn't change directory to `%s': %s",
422 dd->buf + 3, strerror(errno));
428 /* --- Step 2: populate with links --- */
433 for (f = fs; f; f = f->next) {
437 if (lstat(f->name->buf, &st) == 0 && S_ISLNK(st.st_mode))
438 unlink(f->name->buf);
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));
445 } else if (f->name) {
446 if (mkdir(f->name->buf, f->st.st_mode & 07777) &&
448 moan("couldn't create directory `%s%s/%s: %s",
449 e->arch, down.buf, f->name->buf, strerror(errno));
455 /* --- Step 3: move along --- */
463 DPUTS(dd, a->car->arch);
466 if (chdir(dd->buf)) {
467 die(1, "fatal error: couldn't change directory to `%s': %s",
468 dd->buf, strerror(errno));
473 /* --- Interlude: filter out nondirectories from the file list --- *
475 * This is a memory-saving exercise, and it makes the subdirectory handling
483 if (f->name && !f->link)
487 DSPUT(&pool, f->name);
489 DSPUT(&pool, f->link);
496 /* --- Interlude: early exit if no directories --- *
498 * Presumably, a call to @canon@ is cheaper than traversing too many
499 * directories in the kernel.
508 if (chdir(dd->buf)) {
509 die(1, "fatal error: couldn't change directory to `%s': %s",
510 dd->buf, strerror(errno));
517 /* --- Phase four: process subdirectories --- */
520 size_t ulen = up.len, dlen = down.len;
522 /* --- Set current directory for first directory --- *
524 * Subsequent directories do the right thing with the @cont@ argument.
525 * Then just leave this one queued up for the next time around.
532 if (chdir(dd->buf)) {
533 die(1, "fatal error: couldn't change directory to `%s': %s",
534 dd->buf, strerror(errno));
537 /* --- Now just process all the directories in turn --- */
542 /* --- Sort out the new `up' and `down' --- */
547 DPUTD(&down, f->name);
550 /* --- Set up the continuation directory --- */
555 DPUTD(dd, f->next->name);
559 /* --- Clean up this node --- */
562 fent *fnext = f->next;
563 DSPUT(&pool, f->name);
568 /* --- Go for it --- */
580 * Arguments: @const char *f@ = filename to snap
582 * Returns: Zero if ok, nonzero otherwise.
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.
590 static int snap(const char *f)
599 /* --- Open the input file --- */
601 if ((ifd = open(f, O_RDONLY)) < 0) {
602 moan("couldn't open `%s' for reading: %s", f, strerror(errno));
606 if (fstat(ifd, &st)) {
607 moan("couldn't read information about `%s': %s", f, strerror(errno));
611 /* --- Count the architectures --- */
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));
617 /* --- Make the directories needed, remove the old files, and so on --- */
622 char *p = xstrdup(f);
625 for (i = 0; i < narch; 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);
636 for (i = 0; i < narch; i++) {
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));
648 /* --- Main data copy loop --- */
656 n = read(ifd, buf, sizeof(buf));
658 moan("error reading `%s': %s", f, strerror(errno));
660 for (i = 0; i < narch; i++) {
669 for (i = 0; i < narch; i++) {
672 if (write(fd[i], buf, n) < 0) {
673 moan("error writing `%s', %s", d[i].buf, strerror(errno));
683 /* --- Set the state on the finished files --- */
689 u.actime = st.st_atime;
690 u.modtime = st.st_mtime;
692 for (i = 0; i < narch; i++) {
695 chmod(d[i].buf, st.st_mode & 07777);
707 /*----- Subcommands -------------------------------------------------------*/
709 /* --- @sw_link@ --- */
711 int sw_link(int argc, char *argv[])
717 die(1, "Usage: linktree");
719 /* --- Initialize the dynamic strings --- */
722 dstr_puts(&up, "../");
725 dspool_create(&pool, 32);
727 /* --- Set up the architecture lists --- */
729 if (swinfo_fetch(&sw)) {
730 die(1, "couldn't read build status: %s (try running setup)",
734 all = arch_readtab();
735 alist = swbuild_archlist(&sw);
738 moan("All desired architectures already built!");
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));
757 rc = linktree(1, &d);
761 /* --- Clean up the mess --- */
763 dspool_destroy(&pool);
767 /* --- @sw_snap@ --- */
769 int sw_snap(int argc, char *argv[])
776 die(1, "Usage: snaplink FILE...");
778 /* --- Set up the architecture lists --- */
780 if (swinfo_fetch(&sw)) {
781 die(1, "couldn't read build status: %s (try running setup)",
785 alist = swbuild_archlist(&sw);
788 moan("All desired architectures already built!");
792 for (i = 1; i < argc; i++) {
800 /*----- That's all, folks -------------------------------------------------*/