Annotation of src/usr.bin/lndir/lndir.c, Revision 1.23
1.23 ! deraadt 1: /* $OpenBSD: lndir.c,v 1.22 2015/10/10 14:23:12 deraadt Exp $ */
1.1 downsj 2: /* $XConsortium: lndir.c /main/15 1995/08/30 10:56:18 gildea $ */
1.5 ericj 3:
1.15 mickey 4: /*
1.5 ericj 5: * Create shadow link tree (after X11R4 script of the same name)
1.15 mickey 6: * Mark Reinhold (mbr@lcs.mit.edu)/3 January 1990
1.5 ericj 7: */
1.1 downsj 8:
1.15 mickey 9: /*
1.1 downsj 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 <sys/stat.h>
1.12 millert 51:
52: #include <dirent.h>
1.5 ericj 53: #include <err.h>
1.1 downsj 54: #include <errno.h>
55: #include <stdarg.h>
1.12 millert 56: #include <stdio.h>
57: #include <stdlib.h>
1.1 downsj 58: #include <string.h>
1.5 ericj 59: #include <unistd.h>
1.21 deraadt 60: #include <limits.h>
1.5 ericj 61:
1.15 mickey 62: extern char *__progname;
1.1 downsj 63:
1.19 tedu 64: int silent; /* -silent */
65: int ignore_links; /* -ignorelinks */
1.1 downsj 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.19 tedu 80: struct except *exceptions;
1.1 downsj 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.22 deraadt 87:
88: if (pledge("stdio rpath wpath cpath", NULL) == -1)
89: err(1, "pledge");
1.5 ericj 90:
1.10 deraadt 91: while (++argv, --argc) {
92: if ((strcmp(*argv, "-silent") == 0) ||
93: (strcmp(*argv, "-s") == 0))
94: silent = 1;
95: else if ((strcmp(*argv, "-ignorelinks") == 0) ||
96: (strcmp(*argv, "-i") == 0))
97: ignore_links = 1;
98: else if (strcmp(*argv, "-e") == 0) {
99: ++argv, --argc;
100:
101: if (argc < 2)
102: usage();
103: addexcept(*argv);
104: } else if (strcmp(*argv, "--") == 0) {
105: ++argv, --argc;
106: break;
107: } else
108: break;
109: }
110:
111: if (argc < 1 || argc > 2)
112: usage();
113:
114: fn = argv[0];
115: if (argc == 2)
116: tn = argv[1];
117: else
118: tn = ".";
119:
120: /* to directory */
1.23 ! deraadt 121: if (stat(tn, &ts) == -1)
1.10 deraadt 122: err(1, "%s", tn);
123: if (!(S_ISDIR(ts.st_mode)))
1.20 guenther 124: errc(2, ENOTDIR, "%s", tn);
1.23 ! deraadt 125: if (chdir(tn) == -1)
1.10 deraadt 126: err(1, "%s", tn);
127:
128: /* from directory */
1.23 ! deraadt 129: if (stat(fn, &fs) == -1)
1.10 deraadt 130: err(1, "%s", fn);
131: if (!(S_ISDIR(fs.st_mode)))
1.20 guenther 132: errc(2, ENOTDIR, "%s", fn);
1.1 downsj 133:
1.10 deraadt 134: exit(dodir(fn, &fs, &ts, 0));
1.1 downsj 135: }
136:
1.5 ericj 137: int
1.10 deraadt 138: equivalent(char *lname, char *rname)
1.1 downsj 139: {
1.13 millert 140: char *s, *ns;
1.1 downsj 141:
1.12 millert 142: if (strcmp(lname, rname) == 0)
1.10 deraadt 143: return(1);
144: for (s = lname; *s && (s = strchr(s, '/')); s++) {
1.13 millert 145: if (s[1] == '/') {
146: /* collapse multiple slashes in lname */
147: for (ns = s + 1; *ns == '/'; ns++)
148: ;
149: memmove(s + 1, ns, strlen(ns) + 1);
150: }
1.10 deraadt 151: }
1.19 tedu 152: return (strcmp(lname, rname) == 0);
1.1 downsj 153: }
154:
1.5 ericj 155: void
1.10 deraadt 156: addexcept(char *name)
1.1 downsj 157: {
1.10 deraadt 158: struct except *new;
159:
1.19 tedu 160: new = malloc(sizeof(struct except));
161: if (new == NULL)
1.12 millert 162: err(1, NULL);
1.10 deraadt 163: new->name = strdup(name);
1.19 tedu 164: if (new->name == NULL)
1.12 millert 165: err(1, NULL);
1.1 downsj 166:
1.10 deraadt 167: new->next = exceptions;
168: exceptions = new;
1.1 downsj 169: }
170:
171:
1.15 mickey 172: /*
1.5 ericj 173: * Recursively create symbolic links from the current directory to the "from"
174: * directory. Assumes that files described by fs and ts are directories.
175: */
1.10 deraadt 176: #if 0
1.5 ericj 177: char *fn; /* name of "from" directory, either absolute or
1.1 downsj 178: relative to cwd */
1.5 ericj 179: struct stat *fs, *ts; /* stats for the "from" directory and cwd */
180: int rel; /* if true, prepend "../" to fn before using */
1.10 deraadt 181: #endif
182: int
183: dodir(char *fn, struct stat *fs, struct stat *ts, int rel)
1.1 downsj 184: {
1.21 deraadt 185: char buf[PATH_MAX + 1], symbuf[PATH_MAX + 1];
186: char basesym[PATH_MAX + 1];
1.10 deraadt 187: int n_dirs, symlen, basesymlen = -1;
188: struct stat sb, sc;
189: struct except *cur;
190: struct dirent *dp;
191: char *ocurdir, *p;
192: DIR *df;
193:
194: if (fs->st_dev == ts->st_dev && fs->st_ino == ts->st_ino) {
1.12 millert 195: warnx("%s: From and to directories are identical!", fn);
1.10 deraadt 196: return(1);
197: }
198:
199: if (rel)
1.13 millert 200: strlcpy(buf, "../", sizeof(buf));
1.10 deraadt 201: else
202: buf[0] = '\0';
1.13 millert 203: strlcat(buf, fn, sizeof(buf));
1.15 mickey 204:
1.10 deraadt 205: if (!(df = opendir(buf))) {
206: warn("%s: Cannot opendir", buf);
207: return(1);
1.3 downsj 208: }
1.1 downsj 209:
1.10 deraadt 210: p = buf + strlen(buf);
211: *p++ = '/';
212: n_dirs = fs->st_nlink;
213: while ((dp = readdir(df))) {
1.16 matthieu 214: if (dp->d_namlen == 0 || dp->d_name[dp->d_namlen - 1] == '~' ||
215: strncmp(dp->d_name, ".#", 2) == 0)
1.1 downsj 216: continue;
1.14 millert 217: for (cur = exceptions; cur != NULL; cur = cur->next) {
218: if (!strcmp(dp->d_name, cur->name))
1.10 deraadt 219: goto next; /* can't continue */
1.1 downsj 220: }
1.13 millert 221: strlcpy(p, dp->d_name, buf + sizeof(buf) - p);
1.10 deraadt 222:
223: if (n_dirs > 0) {
1.23 ! deraadt 224: if (stat(buf, &sb) == -1) {
1.10 deraadt 225: warn("%s", buf);
226: continue;
227: }
228:
229: if (S_ISDIR(sb.st_mode)) {
230: /* directory */
231: n_dirs--;
232: if (dp->d_name[0] == '.' &&
1.19 tedu 233: (dp->d_name[1] == '\0' ||
234: (dp->d_name[1] == '.' &&
1.10 deraadt 235: dp->d_name[2] == '\0')))
236: continue;
237: if (!strcmp(dp->d_name, "RCS"))
238: continue;
239: if (!strcmp(dp->d_name, "SCCS"))
240: continue;
241: if (!strcmp(dp->d_name, "CVS"))
242: continue;
243: if (!strcmp(dp->d_name, "CVS.adm"))
244: continue;
245: ocurdir = rcurdir;
246: rcurdir = buf;
247: curdir = silent ? buf : NULL;
248: if (!silent)
249: printf("%s:\n", buf);
1.23 ! deraadt 250: if (stat(dp->d_name, &sc) == -1 &&
1.19 tedu 251: errno == ENOENT) {
1.23 ! deraadt 252: if (mkdir(dp->d_name, 0777) == -1 ||
! 253: stat(dp->d_name, &sc) == -1) {
1.10 deraadt 254: warn("%s", dp->d_name);
255: curdir = rcurdir = ocurdir;
256: continue;
257: }
258: }
259: if (readlink(dp->d_name, symbuf,
260: sizeof(symbuf) - 1) >= 0) {
261: fprintf(stderr,
1.19 tedu 262: "%s: is a link instead of a "
263: "directory\n",
1.10 deraadt 264: dp->d_name);
265: curdir = rcurdir = ocurdir;
266: continue;
267: }
1.23 ! deraadt 268: if (chdir(dp->d_name) == -1) {
1.10 deraadt 269: warn("%s", dp->d_name);
270: curdir = rcurdir = ocurdir;
271: continue;
272: }
273: dodir(buf, &sb, &sc, (buf[0] != '/'));
1.23 ! deraadt 274: if (chdir("..") == -1)
1.10 deraadt 275: err(1, "..");
276: curdir = rcurdir = ocurdir;
277: continue;
278: }
1.1 downsj 279: }
1.10 deraadt 280:
281: /* non-directory */
282: symlen = readlink(dp->d_name, symbuf, sizeof(symbuf) - 1);
283: if (symlen >= 0)
284: symbuf[symlen] = '\0';
285:
1.15 mickey 286: /*
1.10 deraadt 287: * The option to ignore links exists mostly because
288: * checking for them slows us down by 10-20%.
1.19 tedu 289: * But it is off by default because this is a useful check.
1.10 deraadt 290: */
291: if (!ignore_links) {
292: /* see if the file in the base tree was a symlink */
1.19 tedu 293: basesymlen = readlink(buf, basesym,
294: sizeof(basesym) - 1);
1.10 deraadt 295: if (basesymlen >= 0)
296: basesym[basesymlen] = '\0';
1.1 downsj 297: }
298:
1.10 deraadt 299: if (symlen >= 0) {
300: /*
301: * Link exists in new tree. Print message if
302: * it doesn't match.
303: */
1.19 tedu 304: if (!equivalent(basesymlen >= 0 ? basesym : buf,
305: symbuf))
1.10 deraadt 306: fprintf(stderr,"%s: %s\n", dp->d_name, symbuf);
307: } else {
1.19 tedu 308: if (symlink(basesymlen >= 0 ? basesym : buf,
1.23 ! deraadt 309: dp->d_name) == -1)
1.10 deraadt 310: warn("%s", dp->d_name);
311: }
312: next:
1.11 millert 313: ;
1.1 downsj 314: }
315:
1.10 deraadt 316: closedir(df);
1.19 tedu 317: return (0);
1.1 downsj 318: }
319:
1.5 ericj 320: void
1.10 deraadt 321: usage(void)
1.1 downsj 322: {
1.19 tedu 323: fprintf(stderr, "usage: %s [-is] [-e exceptfile] fromdir [todir]\n",
1.10 deraadt 324: __progname);
1.5 ericj 325: exit(1);
1.1 downsj 326: }