Deleted Added
sdiff udiff text old ( 78162 ) new ( 79452 )
full compact
1#ifndef lint
2static const char rcsid[] =
3 "$FreeBSD: head/usr.sbin/pkg_install/lib/file.c 78162 2001-06-13 11:55:40Z des $";
4#endif
5
6/*
7 * FreeBSD install - a package for the installation and maintainance
8 * of non-core utilities.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * Jordan K. Hubbard
20 * 18 July 1993
21 *
22 * Miscellaneous file access utilities.
23 *
24 */
25
26#include "lib.h"
27#include <err.h>
28#include <fetch.h>
29#include <pwd.h>
30#include <time.h>
31#include <sys/wait.h>
32
33/* Quick check to see if a file exists */
34Boolean
35fexists(char *fname)
36{
37 struct stat dummy;
38 if (!lstat(fname, &dummy))
39 return TRUE;
40 return FALSE;
41}
42
43/* Quick check to see if something is a directory or symlink to a directory */
44Boolean
45isdir(char *fname)
46{
47 struct stat sb;
48
49 if (lstat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode))
50 return TRUE;
51 else if (lstat(strconcat(fname, "/"), &sb) != FAIL && S_ISDIR(sb.st_mode))
52 return TRUE;
53 else
54 return FALSE;
55}
56
57/* Check to see if file is a dir or symlink to a dir, and is empty */
58Boolean
59isemptydir(char *fname)
60{
61 if (isdir(fname)) {
62 DIR *dirp;
63 struct dirent *dp;
64
65 dirp = opendir(fname);
66 if (!dirp)
67 return FALSE; /* no perms, leave it alone */
68 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
69 if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) {
70 closedir(dirp);
71 return FALSE;
72 }
73 }
74 (void)closedir(dirp);
75 return TRUE;
76 }
77 return FALSE;
78}
79
80/*
81 * Returns TRUE if file is a regular file or symlink pointing to a regular
82 * file
83 */
84Boolean
85isfile(char *fname)
86{
87 struct stat sb;
88 if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode))
89 return TRUE;
90 return FALSE;
91}
92
93/*
94 * Check to see if file is a file or symlink pointing to a file and is empty.
95 * If nonexistent or not a file, say "it's empty", otherwise return TRUE if
96 * zero sized.
97 */
98Boolean
99isemptyfile(char *fname)
100{
101 struct stat sb;
102 if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) {
103 if (sb.st_size != 0)
104 return FALSE;
105 }
106 return TRUE;
107}
108
109/* Returns TRUE if file is a symbolic link. */
110Boolean
111issymlink(char *fname)
112{
113 struct stat sb;
114 if (lstat(fname, &sb) != FAIL && S_ISLNK(sb.st_mode))
115 return TRUE;
116 return FALSE;
117}
118
119/* Returns TRUE if file is a URL specification */
120Boolean
121isURL(char *fname)
122{
123 /*
124 * I'm sure there are other types of URL specifications that I could
125 * also be looking for here, but for now I'll just be happy to get ftp
126 * and http working.
127 */
128 if (!fname)
129 return FALSE;
130 while (isspace(*fname))
131 ++fname;
132 if (!strncmp(fname, "ftp://", 6) || !strncmp(fname, "http://", 7))
133 return TRUE;
134 return FALSE;
135}
136
137#define HOSTNAME_MAX 64
138/*
139 * Try and fetch a file by URL, returning the directory name for where
140 * it's unpacked, if successful.
141 */
142char *
143fileGetURL(char *base, char *spec)
144{
145 char *cp, *rp;
146 char fname[FILENAME_MAX];
147 char pen[FILENAME_MAX];
148 char buf[8192];
149 FILE *ftp;
150 pid_t tpid;
151 int pfd[2], pstat;
152 size_t r, w;
153 char *hint;
154 int fd;
155
156 rp = NULL;
157 /* Special tip that sysinstall left for us */
158 hint = getenv("PKG_ADD_BASE");
159 if (!isURL(spec)) {
160 if (!base && !hint)
161 return NULL;
162 /*
163 * We've been given an existing URL (that's known-good) and now we need
164 * to construct a composite one out of that and the basename we were
165 * handed as a dependency.
166 */
167 if (base) {
168 strcpy(fname, base);
169 /*
170 * Advance back two slashes to get to the root of the package
171 * hierarchy
172 */
173 cp = strrchr(fname, '/');
174 if (cp) {
175 *cp = '\0'; /* chop name */
176 cp = strrchr(fname, '/');
177 }
178 if (cp) {
179 *(cp + 1) = '\0';
180 strcat(cp, "All/");
181 strcat(cp, spec);
182 strcat(cp, ".tgz");
183 }
184 else
185 return NULL;
186 }
187 else {
188 /*
189 * Otherwise, we've been given an environment variable hinting
190 * at the right location from sysinstall
191 */
192 strcpy(fname, hint);
193 strcat(fname, spec);
194 strcat(fname, ".tgz");
195 }
196 }
197 else
198 strcpy(fname, spec);
199
200 if ((ftp = fetchGetURL(fname, Verbose ? "v" : NULL)) == NULL) {
201 printf("Error: FTP Unable to get %s: %s\n",
202 fname, fetchLastErrString);
203 return NULL;
204 }
205
206 if (isatty(0) || Verbose)
207 printf("Fetching %s...", fname), fflush(stdout);
208 pen[0] = '\0';
209 if ((rp = make_playpen(pen, 0)) == NULL) {
210 printf("Error: Unable to construct a new playpen for FTP!\n");
211 fclose(ftp);
212 return NULL;
213 }
214 if (pipe(pfd) == -1) {
215 warn("pipe()");
216 cleanup(0);
217 exit(2);
218 }
219 if ((tpid = fork()) == -1) {
220 warn("pipe()");
221 cleanup(0);
222 exit(2);
223 }
224 if (!tpid) {
225 dup2(pfd[0], 0);
226 for (fd = getdtablesize() - 1; fd >= 3; --fd)
227 close(fd);
228 execl("/usr/bin/tar", "tar", Verbose ? "-xzvf" : "-xzf", "-", 0);
229 _exit(2);
230 }
231 close(pfd[0]);
232 for (;;) {
233 if ((r = fread(buf, 1, sizeof buf, ftp)) < 1)
234 break;
235 if ((w = write(pfd[1], buf, r)) != r)
236 break;
237 }
238 fclose(ftp);
239 close(pfd[1]);
240 if (r == -1)
241 warn("warning: error reading from server");
242 if (w == -1)
243 warn("warning: error writing to tar");
244 tpid = waitpid(tpid, &pstat, 0);
245 if (Verbose)
246 printf("tar command returns %d status\n", WEXITSTATUS(pstat));
247 if (rp && (isatty(0) || Verbose))
248 printf(" Done.\n");
249 return rp;
250}
251
252char *
253fileFindByPath(char *base, char *fname)
254{
255 static char tmp[FILENAME_MAX];
256 char *cp;
257 char *suffixes[] = {".tgz", ".tar", ".tbz2", NULL};
258 int i;
259
260 if (fexists(fname) && isfile(fname)) {
261 strcpy(tmp, fname);
262 return tmp;
263 }
264 if (base) {
265 strcpy(tmp, base);
266
267 cp = strrchr(tmp, '/');
268 if (cp) {
269 *cp = '\0'; /* chop name */
270 cp = strrchr(tmp, '/');
271 }
272 if (cp)
273 for (i = 0; suffixes[i] != NULL; i++) {
274 *(cp + 1) = '\0';
275 strcat(cp, "All/");
276 strcat(cp, fname);
277 strcat(cp, suffixes[i]);
278 if (fexists(tmp))
279 return tmp;
280 }
281 }
282
283 cp = getenv("PKG_PATH");
284 while (cp) {
285 char *cp2 = strsep(&cp, ":");
286
287 for (i = 0; suffixes[i] != NULL; i++) {
288 snprintf(tmp, FILENAME_MAX, "%s/%s%s", cp2 ? cp2 : cp, fname, suffixes[i]);
289 if (fexists(tmp) && isfile(tmp))
290 return tmp;
291 }
292 }
293 return NULL;
294}
295
296char *
297fileGetContents(char *fname)
298{
299 char *contents;
300 struct stat sb;
301 int fd;
302
303 if (stat(fname, &sb) == FAIL) {
304 cleanup(0);
305 errx(2, __FUNCTION__ ": can't stat '%s'", fname);
306 }
307
308 contents = (char *)malloc(sb.st_size + 1);
309 fd = open(fname, O_RDONLY, 0);
310 if (fd == FAIL) {
311 cleanup(0);
312 errx(2, __FUNCTION__ ": unable to open '%s' for reading", fname);
313 }
314 if (read(fd, contents, sb.st_size) != sb.st_size) {
315 cleanup(0);
316 errx(2, __FUNCTION__ ": short read on '%s' - did not get %qd bytes",
317 fname, sb.st_size);
318 }
319 close(fd);
320 contents[sb.st_size] = '\0';
321 return contents;
322}
323
324/*
325 * Takes a filename and package name, returning (in "try") the
326 * canonical "preserve" name for it.
327 */
328Boolean
329make_preserve_name(char *try, int max, char *name, char *file)
330{
331 int len, i;
332
333 if ((len = strlen(file)) == 0)
334 return FALSE;
335 else
336 i = len - 1;
337 strncpy(try, file, max);
338 if (try[i] == '/') /* Catch trailing slash early and save checking in the loop */
339 --i;
340 for (; i; i--) {
341 if (try[i] == '/') {
342 try[i + 1]= '.';
343 strncpy(&try[i + 2], &file[i + 1], max - i - 2);
344 break;
345 }
346 }
347 if (!i) {
348 try[0] = '.';
349 strncpy(try + 1, file, max - 1);
350 }
351 /* I should probably be called rude names for these inline assignments */
352 strncat(try, ".", max -= strlen(try));
353 strncat(try, name, max -= strlen(name));
354 strncat(try, ".", max--);
355 strncat(try, "backup", max -= 6);
356 return TRUE;
357}
358
359/* Write the contents of "str" to a file */
360void
361write_file(char *name, char *str)
362{
363 FILE *fp;
364 int len;
365
366 fp = fopen(name, "w");
367 if (!fp) {
368 cleanup(0);
369 errx(2, __FUNCTION__ ": cannot fopen '%s' for writing", name);
370 }
371 len = strlen(str);
372 if (fwrite(str, 1, len, fp) != len) {
373 cleanup(0);
374 errx(2, __FUNCTION__ ": short fwrite on '%s', tried to write %d bytes", name, len);
375 }
376 if (fclose(fp)) {
377 cleanup(0);
378 errx(2, __FUNCTION__ ": failure to fclose '%s'", name);
379 }
380}
381
382void
383copy_file(char *dir, char *fname, char *to)
384{
385 char cmd[FILENAME_MAX];
386
387 if (fname[0] == '/')
388 snprintf(cmd, FILENAME_MAX, "cp -r %s %s", fname, to);
389 else
390 snprintf(cmd, FILENAME_MAX, "cp -r %s/%s %s", dir, fname, to);
391 if (vsystem(cmd)) {
392 cleanup(0);
393 errx(2, __FUNCTION__ ": could not perform '%s'", cmd);
394 }
395}
396
397void
398move_file(char *dir, char *fname, char *to)
399{
400 char cmd[FILENAME_MAX];
401
402 if (fname[0] == '/')
403 snprintf(cmd, FILENAME_MAX, "mv %s %s", fname, to);
404 else
405 snprintf(cmd, FILENAME_MAX, "mv %s/%s %s", dir, fname, to);
406 if (vsystem(cmd)) {
407 cleanup(0);
408 errx(2, __FUNCTION__ ": could not perform '%s'", cmd);
409 }
410}
411
412/*
413 * Copy a hierarchy (possibly from dir) to the current directory, or
414 * if "to" is TRUE, from the current directory to a location someplace
415 * else.
416 *
417 * Though slower, using tar to copy preserves symlinks and everything
418 * without me having to write some big hairy routine to do it.
419 */
420void
421copy_hierarchy(char *dir, char *fname, Boolean to)
422{
423 char cmd[FILENAME_MAX * 3];
424
425 if (!to) {
426 /* If absolute path, use it */
427 if (*fname == '/')
428 dir = "/";
429 snprintf(cmd, FILENAME_MAX * 3, "tar cf - -C %s %s | tar xpf -",
430 dir, fname);
431 }
432 else
433 snprintf(cmd, FILENAME_MAX * 3, "tar cf - %s | tar xpf - -C %s",
434 fname, dir);
435#ifdef DEBUG
436 printf("Using '%s' to copy trees.\n", cmd);
437#endif
438 if (system(cmd)) {
439 cleanup(0);
440 errx(2, __FUNCTION__ ": could not perform '%s'", cmd);
441 }
442}
443
444/* Unpack a tar file */
445int
446unpack(char *pkg, char *flist)
447{
448 char args[10], suffix[80], *cp;
449
450 args[0] = '\0';
451 /*
452 * Figure out by a crude heuristic whether this or not this is probably
453 * compressed and whichever compression utility was used (gzip or bzip2).
454 */
455 if (strcmp(pkg, "-")) {
456 cp = strrchr(pkg, '.');
457 if (cp) {
458 strcpy(suffix, cp + 1);
459 if (strchr(suffix, 'z') || strchr(suffix, 'Z')) {
460 if (strchr(suffix, 'b'))
461 strcpy(args, "-y");
462 else
463 strcpy(args, "-z");
464 }
465 }
466 }
467 else
468 strcpy(args, "-z");
469 strcat(args, " -xpf");
470 if (vsystem("tar %s '%s' %s", args, pkg, flist ? flist : "")) {
471 warnx("tar extract of %s failed!", pkg);
472 return 1;
473 }
474 return 0;
475}
476
477/*
478 * Using fmt, replace all instances of:
479 *
480 * %F With the parameter "name"
481 * %D With the parameter "dir"
482 * %B Return the directory part ("base") of %D/%F
483 * %f Return the filename part of %D/%F
484 *
485 * Does not check for overflow - caution!
486 *
487 */
488void
489format_cmd(char *buf, char *fmt, char *dir, char *name)
490{
491 char *cp, scratch[FILENAME_MAX * 2];
492
493 while (*fmt) {
494 if (*fmt == '%') {
495 switch (*++fmt) {
496 case 'F':
497 strcpy(buf, name);
498 buf += strlen(name);
499 break;
500
501 case 'D':
502 strcpy(buf, dir);
503 buf += strlen(dir);
504 break;
505
506 case 'B':
507 sprintf(scratch, "%s/%s", dir, name);
508 cp = &scratch[strlen(scratch) - 1];
509 while (cp != scratch && *cp != '/')
510 --cp;
511 *cp = '\0';
512 strcpy(buf, scratch);
513 buf += strlen(scratch);
514 break;
515
516 case 'f':
517 sprintf(scratch, "%s/%s", dir, name);
518 cp = &scratch[strlen(scratch) - 1];
519 while (cp != scratch && *(cp - 1) != '/')
520 --cp;
521 strcpy(buf, cp);
522 buf += strlen(cp);
523 break;
524
525 default:
526 *buf++ = *fmt;
527 break;
528 }
529 ++fmt;
530 }
531 else
532 *buf++ = *fmt++;
533 }
534 *buf = '\0';
535}