Annotation of src/usr.bin/lndir/lndir.c, Revision 1.10
1.10 ! deraadt 1: /* $OpenBSD: lndir.c,v 1.9 2002/05/29 18:33:39 deraadt Exp $ */
1.1 downsj 2: /* $XConsortium: lndir.c /main/15 1995/08/30 10:56:18 gildea $ */
1.5 ericj 3:
4: /*
5: * Create shadow link tree (after X11R4 script of the same name)
6: * Mark Reinhold (mbr@lcs.mit.edu)/3 January 1990
7: */
1.1 downsj 8:
9: /*
10: Copyright (c) 1990, X Consortium
11:
12: Permission is hereby granted, free of charge, to any person obtaining a copy
13: of this software and associated documentation files (the "Software"), to deal
14: in the Software without restriction, including without limitation the rights
15: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16: copies of the Software, and to permit persons to whom the Software is
17: furnished to do so, subject to the following conditions:
18:
19: The above copyright notice and this permission notice shall be included in
20: all copies or substantial portions of the Software.
21:
22: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25: X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
26: AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
27: CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28:
29: Except as contained in this notice, the name of the X Consortium shall not be
30: used in advertising or otherwise to promote the sale, use or other dealings
31: in this Software without prior written authorization from the X Consortium.
32:
33: */
34:
35: /* From the original /bin/sh script:
36:
37: Used to create a copy of the a directory tree that has links for all
38: non-directories (except those named RCS, SCCS or CVS.adm). If you are
39: building the distribution on more than one machine, you should use
40: this technique.
41:
42: If your master sources are located in /usr/local/src/X and you would like
43: your link tree to be in /usr/local/src/new-X, do the following:
44:
1.10 ! deraadt 45: % mkdir /usr/local/src/new-X
1.1 downsj 46: % cd /usr/local/src/new-X
1.10 ! deraadt 47: % lndir ../X
1.1 downsj 48: */
49:
50: #include <stdio.h>
1.4 niklas 51: #include <stdlib.h>
1.1 downsj 52: #include <sys/types.h>
53: #include <sys/stat.h>
54: #include <sys/param.h>
1.5 ericj 55: #include <err.h>
1.1 downsj 56: #include <errno.h>
57: #include <dirent.h>
58: #include <stdarg.h>
59: #include <string.h>
1.5 ericj 60: #include <unistd.h>
61:
62: extern char *__progname;
1.1 downsj 63:
64: int silent = 0; /* -silent */
65: int ignore_links = 0; /* -ignorelinks */
66:
67: char *rcurdir;
68: char *curdir;
69:
1.7 millert 70: int equivalent(char *, char *);
71: void addexcept(char *);
72: int dodir(char *, struct stat *, struct stat *, int);
73: void usage(void);
1.5 ericj 74:
1.1 downsj 75: struct except {
76: char *name;
77: struct except *next;
78: };
1.5 ericj 79:
1.1 downsj 80: struct except *exceptions = (struct except *)NULL;
81:
1.5 ericj 82: int
1.10 ! deraadt 83: main(int argc, char *argv[])
1.1 downsj 84: {
1.10 ! deraadt 85: struct stat fs, ts;
1.6 jasoni 86: char *fn, *tn;
1.5 ericj 87:
1.10 ! deraadt 88: while (++argv, --argc) {
! 89: if ((strcmp(*argv, "-silent") == 0) ||
! 90: (strcmp(*argv, "-s") == 0))
! 91: silent = 1;
! 92: else if ((strcmp(*argv, "-ignorelinks") == 0) ||
! 93: (strcmp(*argv, "-i") == 0))
! 94: ignore_links = 1;
! 95: else if (strcmp(*argv, "-e") == 0) {
! 96: ++argv, --argc;
! 97:
! 98: if (argc < 2)
! 99: usage();
! 100: addexcept(*argv);
! 101: } else if (strcmp(*argv, "--") == 0) {
! 102: ++argv, --argc;
! 103: break;
! 104: } else
! 105: break;
! 106: }
! 107:
! 108: if (argc < 1 || argc > 2)
! 109: usage();
! 110:
! 111: fn = argv[0];
! 112: if (argc == 2)
! 113: tn = argv[1];
! 114: else
! 115: tn = ".";
! 116:
! 117: /* to directory */
! 118: if (stat(tn, &ts) < 0)
! 119: err(1, "%s", tn);
! 120: if (!(S_ISDIR(ts.st_mode)))
! 121: errx(2, "%s: %s", tn, strerror(ENOTDIR));
! 122: if (chdir(tn) < 0)
! 123: err(1, "%s", tn);
! 124:
! 125: /* from directory */
! 126: if (stat(fn, &fs) < 0)
! 127: err(1, "%s", fn);
! 128: if (!(S_ISDIR(fs.st_mode)))
! 129: err(2, "%s: %s", fn, strerror(ENOTDIR));
1.1 downsj 130:
1.10 ! deraadt 131: exit(dodir(fn, &fs, &ts, 0));
1.1 downsj 132: }
133:
1.5 ericj 134: int
1.10 ! deraadt 135: equivalent(char *lname, char *rname)
1.1 downsj 136: {
1.10 ! deraadt 137: char *s;
1.1 downsj 138:
1.10 ! deraadt 139: if (!strcmp(lname, rname))
! 140: return(1);
! 141: for (s = lname; *s && (s = strchr(s, '/')); s++) {
! 142: while (s[1] == '/')
! 143: strcpy(s+1, s+2);
! 144: }
! 145: return(!strcmp(lname, rname));
1.1 downsj 146: }
147:
1.5 ericj 148: void
1.10 ! deraadt 149: addexcept(char *name)
1.1 downsj 150: {
1.10 ! deraadt 151: struct except *new;
! 152:
! 153: new = (struct except *)malloc(sizeof(struct except));
! 154: if (new == (struct except *)NULL)
! 155: err(1, "addexcept");
! 156: new->name = strdup(name);
! 157: if (new->name == (char *)NULL)
! 158: err(1, "addexcept");
1.1 downsj 159:
1.10 ! deraadt 160: new->next = exceptions;
! 161: exceptions = new;
1.1 downsj 162: }
163:
164:
1.5 ericj 165: /*
166: * Recursively create symbolic links from the current directory to the "from"
167: * directory. Assumes that files described by fs and ts are directories.
168: */
1.10 ! deraadt 169: #if 0
1.5 ericj 170: char *fn; /* name of "from" directory, either absolute or
1.1 downsj 171: relative to cwd */
1.5 ericj 172: struct stat *fs, *ts; /* stats for the "from" directory and cwd */
173: int rel; /* if true, prepend "../" to fn before using */
1.10 ! deraadt 174: #endif
! 175: int
! 176: dodir(char *fn, struct stat *fs, struct stat *ts, int rel)
1.1 downsj 177: {
1.10 ! deraadt 178: char buf[MAXPATHLEN + 1], symbuf[MAXPATHLEN + 1], basesym[MAXPATHLEN + 1];
! 179: int n_dirs, symlen, basesymlen = -1;
! 180: struct stat sb, sc;
! 181: struct except *cur;
! 182: struct dirent *dp;
! 183: char *ocurdir, *p;
! 184: DIR *df;
! 185:
! 186: if (fs->st_dev == ts->st_dev && fs->st_ino == ts->st_ino) {
! 187: warn("%s: From and to directories are identical!", fn);
! 188: return(1);
! 189: }
! 190:
! 191: if (rel)
! 192: strlcpy(buf, "../", sizeof buf);
! 193: else
! 194: buf[0] = '\0';
! 195: strlcat(buf, fn, sizeof buf);
1.1 downsj 196:
1.10 ! deraadt 197: if (!(df = opendir(buf))) {
! 198: warn("%s: Cannot opendir", buf);
! 199: return(1);
1.3 downsj 200: }
1.1 downsj 201:
1.10 ! deraadt 202: p = buf + strlen(buf);
! 203: *p++ = '/';
! 204: n_dirs = fs->st_nlink;
! 205: while ((dp = readdir(df))) {
! 206: if (dp->d_name[strlen(dp->d_name) - 1] == '~')
1.1 downsj 207: continue;
1.10 ! deraadt 208: for (cur = exceptions; cur != (struct except *)NULL;
! 209: cur = cur->next) {
! 210: if (!strcmp (dp->d_name, cur->name))
! 211: goto next; /* can't continue */
1.1 downsj 212: }
1.10 ! deraadt 213: strcpy(p, dp->d_name);
! 214:
! 215: if (n_dirs > 0) {
! 216: if (stat(buf, &sb) < 0) {
! 217: warn("%s", buf);
! 218: continue;
! 219: }
! 220:
! 221: if (S_ISDIR(sb.st_mode)) {
! 222: /* directory */
! 223: n_dirs--;
! 224: if (dp->d_name[0] == '.' &&
! 225: (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' &&
! 226: dp->d_name[2] == '\0')))
! 227: continue;
! 228: if (!strcmp(dp->d_name, "RCS"))
! 229: continue;
! 230: if (!strcmp(dp->d_name, "SCCS"))
! 231: continue;
! 232: if (!strcmp(dp->d_name, "CVS"))
! 233: continue;
! 234: if (!strcmp(dp->d_name, "CVS.adm"))
! 235: continue;
! 236: ocurdir = rcurdir;
! 237: rcurdir = buf;
! 238: curdir = silent ? buf : NULL;
! 239: if (!silent)
! 240: printf("%s:\n", buf);
! 241: if (stat(dp->d_name, &sc) < 0 && errno == ENOENT) {
! 242: if (mkdir(dp->d_name, 0777) < 0 ||
! 243: stat(dp->d_name, &sc) < 0) {
! 244: warn("%s", dp->d_name);
! 245: curdir = rcurdir = ocurdir;
! 246: continue;
! 247: }
! 248: }
! 249: if (readlink(dp->d_name, symbuf,
! 250: sizeof(symbuf) - 1) >= 0) {
! 251: fprintf(stderr,
! 252: "%s: is a link instead of a directory\n",
! 253: dp->d_name);
! 254: curdir = rcurdir = ocurdir;
! 255: continue;
! 256: }
! 257: if (chdir(dp->d_name) < 0) {
! 258: warn("%s", dp->d_name);
! 259: curdir = rcurdir = ocurdir;
! 260: continue;
! 261: }
! 262: dodir(buf, &sb, &sc, (buf[0] != '/'));
! 263: if (chdir("..") < 0)
! 264: err(1, "..");
! 265: curdir = rcurdir = ocurdir;
! 266: continue;
! 267: }
1.1 downsj 268: }
1.10 ! deraadt 269:
! 270: /* non-directory */
! 271: symlen = readlink(dp->d_name, symbuf, sizeof(symbuf) - 1);
! 272: if (symlen >= 0)
! 273: symbuf[symlen] = '\0';
! 274:
! 275: /*
! 276: * The option to ignore links exists mostly because
! 277: * checking for them slows us down by 10-20%.
! 278: * But it is off by default because this really is a useful check.
! 279: */
! 280: if (!ignore_links) {
! 281: /* see if the file in the base tree was a symlink */
! 282: basesymlen = readlink(buf, basesym, sizeof(basesym) - 1);
! 283: if (basesymlen >= 0)
! 284: basesym[basesymlen] = '\0';
1.1 downsj 285: }
286:
1.10 ! deraadt 287: if (symlen >= 0) {
! 288: /*
! 289: * Link exists in new tree. Print message if
! 290: * it doesn't match.
! 291: */
! 292: if (!equivalent(basesymlen>=0 ? basesym : buf, symbuf))
! 293: fprintf(stderr,"%s: %s\n", dp->d_name, symbuf);
! 294: } else {
! 295: if (symlink(basesymlen>=0 ? basesym : buf, dp->d_name) < 0)
! 296: warn("%s", dp->d_name);
! 297: }
! 298: next:
1.1 downsj 299: }
300:
1.10 ! deraadt 301: closedir(df);
! 302: return(0);
1.1 downsj 303: }
304:
1.5 ericj 305: void
1.10 ! deraadt 306: usage(void)
1.1 downsj 307: {
1.5 ericj 308: (void)fprintf(stderr, "usage: %s [-e except] [-si] fromdir todir\n",
1.10 ! deraadt 309: __progname);
1.5 ericj 310: exit(1);
1.1 downsj 311: }