Deleted Added
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 $";
3 "$FreeBSD: head/usr.sbin/pkg_install/lib/file.c 79452 2001-07-09 09:24:06Z brian $";
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);
228 execl("/usr/bin/tar", "tar", Verbose ? "-xzvf" : "-xzf", "-",
229 (char *)0);
230 _exit(2);
231 }
232 close(pfd[0]);
233 for (;;) {
234 if ((r = fread(buf, 1, sizeof buf, ftp)) < 1)
235 break;
236 if ((w = write(pfd[1], buf, r)) != r)
237 break;
238 }
239 fclose(ftp);
240 close(pfd[1]);
241 if (r == -1)
242 warn("warning: error reading from server");
243 if (w == -1)
244 warn("warning: error writing to tar");
245 tpid = waitpid(tpid, &pstat, 0);
246 if (Verbose)
247 printf("tar command returns %d status\n", WEXITSTATUS(pstat));
248 if (rp && (isatty(0) || Verbose))
249 printf(" Done.\n");
250 return rp;
251}
252
253char *
254fileFindByPath(char *base, char *fname)
255{
256 static char tmp[FILENAME_MAX];
257 char *cp;
258 char *suffixes[] = {".tgz", ".tar", ".tbz2", NULL};
259 int i;
260
261 if (fexists(fname) && isfile(fname)) {
262 strcpy(tmp, fname);
263 return tmp;
264 }
265 if (base) {
266 strcpy(tmp, base);
267
268 cp = strrchr(tmp, '/');
269 if (cp) {
270 *cp = '\0'; /* chop name */
271 cp = strrchr(tmp, '/');
272 }
273 if (cp)
274 for (i = 0; suffixes[i] != NULL; i++) {
275 *(cp + 1) = '\0';
276 strcat(cp, "All/");
277 strcat(cp, fname);
278 strcat(cp, suffixes[i]);
279 if (fexists(tmp))
280 return tmp;
281 }
282 }
283
284 cp = getenv("PKG_PATH");
285 while (cp) {
286 char *cp2 = strsep(&cp, ":");
287
288 for (i = 0; suffixes[i] != NULL; i++) {
289 snprintf(tmp, FILENAME_MAX, "%s/%s%s", cp2 ? cp2 : cp, fname, suffixes[i]);
290 if (fexists(tmp) && isfile(tmp))
291 return tmp;
292 }
293 }
294 return NULL;
295}
296
297char *
298fileGetContents(char *fname)
299{
300 char *contents;
301 struct stat sb;
302 int fd;
303
304 if (stat(fname, &sb) == FAIL) {
305 cleanup(0);
306 errx(2, __FUNCTION__ ": can't stat '%s'", fname);
307 }
308
309 contents = (char *)malloc(sb.st_size + 1);
310 fd = open(fname, O_RDONLY, 0);
311 if (fd == FAIL) {
312 cleanup(0);
313 errx(2, __FUNCTION__ ": unable to open '%s' for reading", fname);
314 }
315 if (read(fd, contents, sb.st_size) != sb.st_size) {
316 cleanup(0);
317 errx(2, __FUNCTION__ ": short read on '%s' - did not get %qd bytes",
318 fname, sb.st_size);
319 }
320 close(fd);
321 contents[sb.st_size] = '\0';
322 return contents;
323}
324
325/*
326 * Takes a filename and package name, returning (in "try") the
327 * canonical "preserve" name for it.
328 */
329Boolean
330make_preserve_name(char *try, int max, char *name, char *file)
331{
332 int len, i;
333
334 if ((len = strlen(file)) == 0)
335 return FALSE;
336 else
337 i = len - 1;
338 strncpy(try, file, max);
339 if (try[i] == '/') /* Catch trailing slash early and save checking in the loop */
340 --i;
341 for (; i; i--) {
342 if (try[i] == '/') {
343 try[i + 1]= '.';
344 strncpy(&try[i + 2], &file[i + 1], max - i - 2);
345 break;
346 }
347 }
348 if (!i) {
349 try[0] = '.';
350 strncpy(try + 1, file, max - 1);
351 }
352 /* I should probably be called rude names for these inline assignments */
353 strncat(try, ".", max -= strlen(try));
354 strncat(try, name, max -= strlen(name));
355 strncat(try, ".", max--);
356 strncat(try, "backup", max -= 6);
357 return TRUE;
358}
359
360/* Write the contents of "str" to a file */
361void
362write_file(char *name, char *str)
363{
364 FILE *fp;
365 int len;
366
367 fp = fopen(name, "w");
368 if (!fp) {
369 cleanup(0);
370 errx(2, __FUNCTION__ ": cannot fopen '%s' for writing", name);
371 }
372 len = strlen(str);
373 if (fwrite(str, 1, len, fp) != len) {
374 cleanup(0);
375 errx(2, __FUNCTION__ ": short fwrite on '%s', tried to write %d bytes", name, len);
376 }
377 if (fclose(fp)) {
378 cleanup(0);
379 errx(2, __FUNCTION__ ": failure to fclose '%s'", name);
380 }
381}
382
383void
384copy_file(char *dir, char *fname, char *to)
385{
386 char cmd[FILENAME_MAX];
387
388 if (fname[0] == '/')
389 snprintf(cmd, FILENAME_MAX, "cp -r %s %s", fname, to);
390 else
391 snprintf(cmd, FILENAME_MAX, "cp -r %s/%s %s", dir, fname, to);
392 if (vsystem(cmd)) {
393 cleanup(0);
394 errx(2, __FUNCTION__ ": could not perform '%s'", cmd);
395 }
396}
397
398void
399move_file(char *dir, char *fname, char *to)
400{
401 char cmd[FILENAME_MAX];
402
403 if (fname[0] == '/')
404 snprintf(cmd, FILENAME_MAX, "mv %s %s", fname, to);
405 else
406 snprintf(cmd, FILENAME_MAX, "mv %s/%s %s", dir, fname, to);
407 if (vsystem(cmd)) {
408 cleanup(0);
409 errx(2, __FUNCTION__ ": could not perform '%s'", cmd);
410 }
411}
412
413/*
414 * Copy a hierarchy (possibly from dir) to the current directory, or
415 * if "to" is TRUE, from the current directory to a location someplace
416 * else.
417 *
418 * Though slower, using tar to copy preserves symlinks and everything
419 * without me having to write some big hairy routine to do it.
420 */
421void
422copy_hierarchy(char *dir, char *fname, Boolean to)
423{
424 char cmd[FILENAME_MAX * 3];
425
426 if (!to) {
427 /* If absolute path, use it */
428 if (*fname == '/')
429 dir = "/";
430 snprintf(cmd, FILENAME_MAX * 3, "tar cf - -C %s %s | tar xpf -",
431 dir, fname);
432 }
433 else
434 snprintf(cmd, FILENAME_MAX * 3, "tar cf - %s | tar xpf - -C %s",
435 fname, dir);
436#ifdef DEBUG
437 printf("Using '%s' to copy trees.\n", cmd);
438#endif
439 if (system(cmd)) {
440 cleanup(0);
441 errx(2, __FUNCTION__ ": could not perform '%s'", cmd);
442 }
443}
444
445/* Unpack a tar file */
446int
447unpack(char *pkg, char *flist)
448{
449 char args[10], suffix[80], *cp;
450
451 args[0] = '\0';
452 /*
453 * Figure out by a crude heuristic whether this or not this is probably
454 * compressed and whichever compression utility was used (gzip or bzip2).
455 */
456 if (strcmp(pkg, "-")) {
457 cp = strrchr(pkg, '.');
458 if (cp) {
459 strcpy(suffix, cp + 1);
460 if (strchr(suffix, 'z') || strchr(suffix, 'Z')) {
461 if (strchr(suffix, 'b'))
462 strcpy(args, "-y");
463 else
464 strcpy(args, "-z");
465 }
466 }
467 }
468 else
469 strcpy(args, "-z");
470 strcat(args, " -xpf");
471 if (vsystem("tar %s '%s' %s", args, pkg, flist ? flist : "")) {
472 warnx("tar extract of %s failed!", pkg);
473 return 1;
474 }
475 return 0;
476}
477
478/*
479 * Using fmt, replace all instances of:
480 *
481 * %F With the parameter "name"
482 * %D With the parameter "dir"
483 * %B Return the directory part ("base") of %D/%F
484 * %f Return the filename part of %D/%F
485 *
486 * Does not check for overflow - caution!
487 *
488 */
489void
490format_cmd(char *buf, char *fmt, char *dir, char *name)
491{
492 char *cp, scratch[FILENAME_MAX * 2];
493
494 while (*fmt) {
495 if (*fmt == '%') {
496 switch (*++fmt) {
497 case 'F':
498 strcpy(buf, name);
499 buf += strlen(name);
500 break;
501
502 case 'D':
503 strcpy(buf, dir);
504 buf += strlen(dir);
505 break;
506
507 case 'B':
508 sprintf(scratch, "%s/%s", dir, name);
509 cp = &scratch[strlen(scratch) - 1];
510 while (cp != scratch && *cp != '/')
511 --cp;
512 *cp = '\0';
513 strcpy(buf, scratch);
514 buf += strlen(scratch);
515 break;
516
517 case 'f':
518 sprintf(scratch, "%s/%s", dir, name);
519 cp = &scratch[strlen(scratch) - 1];
520 while (cp != scratch && *(cp - 1) != '/')
521 --cp;
522 strcpy(buf, cp);
523 buf += strlen(cp);
524 break;
525
526 default:
527 *buf++ = *fmt;
528 break;
529 }
530 ++fmt;
531 }
532 else
533 *buf++ = *fmt++;
534 }
535 *buf = '\0';
536}