int pkg_create_repo(char *path, bool force, bool filelist, void (progress)(struct pkg *pkg, void *data), void *data) { FTS *fts = NULL; struct thd_data thd_data; int num_workers; size_t len; pthread_t *tids = NULL; struct digest_list_entry *dlist = NULL, *cur_dig, *dtmp; sqlite3 *sqlite = NULL; char *errmsg = NULL; int retcode = EPKG_OK; char *repopath[2]; char repodb[MAXPATHLEN + 1]; char repopack[MAXPATHLEN + 1]; char *manifest_digest; FILE *psyml, *fsyml, *mandigests; psyml = fsyml = mandigests = NULL; if (!is_dir(path)) { pkg_emit_error("%s is not a directory", path); return (EPKG_FATAL); } repopath[0] = path; repopath[1] = NULL; len = sizeof(num_workers); if (sysctlbyname("hw.ncpu", &num_workers, &len, NULL, 0) == -1) num_workers = 6; if ((fts = fts_open(repopath, FTS_PHYSICAL|FTS_NOCHDIR, NULL)) == NULL) { pkg_emit_errno("fts_open", path); retcode = EPKG_FATAL; goto cleanup; } snprintf(repodb, sizeof(repodb), "%s/%s", path, repo_packagesite_file); if ((psyml = fopen(repodb, "w")) == NULL) { retcode = EPKG_FATAL; goto cleanup; } if (filelist) { snprintf(repodb, sizeof(repodb), "%s/%s", path, repo_filesite_file); if ((fsyml = fopen(repodb, "w")) == NULL) { retcode = EPKG_FATAL; goto cleanup; } } snprintf(repodb, sizeof(repodb), "%s/%s", path, repo_digests_file); if ((mandigests = fopen(repodb, "w")) == NULL) { retcode = EPKG_FATAL; goto cleanup; } snprintf(repodb, sizeof(repodb), "%s/%s", path, repo_db_file); snprintf(repopack, sizeof(repopack), "%s/repo.txz", path); pack_extract(repopack, repo_db_file, repodb); if ((retcode = pkgdb_repo_open(repodb, force, &sqlite, true)) != EPKG_OK) goto cleanup; if ((retcode = pkgdb_repo_init(sqlite, true)) != EPKG_OK) goto cleanup; thd_data.root_path = path; thd_data.max_results = num_workers; thd_data.num_results = 0; thd_data.stop = false; thd_data.fts = fts; thd_data.read_files = filelist; pthread_mutex_init(&thd_data.fts_m, NULL); thd_data.results = NULL; thd_data.thd_finished = 0; pthread_mutex_init(&thd_data.results_m, NULL); pthread_cond_init(&thd_data.has_result, NULL); pthread_cond_init(&thd_data.has_room, NULL); /* Launch workers */ tids = calloc(num_workers, sizeof(pthread_t)); for (int i = 0; i < num_workers; i++) { pthread_create(&tids[i], NULL, (void *)&read_pkg_file, &thd_data); } for (;;) { struct pkg_result *r; const char *origin; long manifest_pos, files_pos; pthread_mutex_lock(&thd_data.results_m); while ((r = thd_data.results) == NULL) { if (thd_data.thd_finished == num_workers) { break; } pthread_cond_wait(&thd_data.has_result, &thd_data.results_m); } if (r != NULL) { LL_DELETE(thd_data.results, thd_data.results); thd_data.num_results--; pthread_cond_signal(&thd_data.has_room); } pthread_mutex_unlock(&thd_data.results_m); if (r == NULL) { break; } if (r->retcode != EPKG_OK) { continue; } /* do not add if package if already in repodb (possibly at a different pkg_path) */ retcode = pkgdb_repo_cksum_exists(sqlite, r->cksum); if (retcode == EPKG_FATAL) { goto cleanup; } else if (retcode == EPKG_OK) { continue; } if (progress != NULL) progress(r->pkg, data); manifest_pos = ftell(psyml); pkg_emit_manifest_file(r->pkg, psyml, PKG_MANIFEST_EMIT_COMPACT, &manifest_digest); if (filelist) { files_pos = ftell(fsyml); pkg_emit_filelist(r->pkg, fsyml); } else { files_pos = 0; } pkg_get(r->pkg, PKG_ORIGIN, &origin); cur_dig = malloc(sizeof (struct digest_list_entry)); cur_dig->origin = strdup(origin); cur_dig->digest = manifest_digest; cur_dig->manifest_pos = manifest_pos; cur_dig->files_pos = files_pos; LL_PREPEND(dlist, cur_dig); retcode = pkgdb_repo_add_package(r->pkg, r->path, sqlite, manifest_digest, false, true); if (retcode == EPKG_END) { continue; } else if (retcode != EPKG_OK) { goto cleanup; } pkg_free(r->pkg); free(r); } /* Now sort all digests */ LL_SORT(dlist, digest_sort_compare_func); cleanup: if (pkgdb_repo_close(sqlite, retcode == EPKG_OK) != EPKG_OK) { retcode = EPKG_FATAL; } LL_FOREACH_SAFE(dlist, cur_dig, dtmp) { if (retcode == EPKG_OK) { fprintf(mandigests, "%s:%s:%ld:%ld\n", cur_dig->origin, cur_dig->digest, cur_dig->manifest_pos, cur_dig->files_pos); } free(cur_dig->digest); free(cur_dig->origin); free(cur_dig); } if (tids != NULL) { // Cancel running threads if (retcode != EPKG_OK) { pthread_mutex_lock(&thd_data.fts_m); thd_data.stop = true; pthread_mutex_unlock(&thd_data.fts_m); } // Join on threads to release thread IDs for (int i = 0; i < num_workers; i++) { pthread_join(tids[i], NULL); } free(tids); } if (fts != NULL) fts_close(fts); if (fsyml != NULL) fclose(fsyml); if (psyml != NULL) fclose(psyml); if (mandigests != NULL) fclose(mandigests); if (sqlite != NULL) sqlite3_close(sqlite); if (errmsg != NULL) sqlite3_free(errmsg); sqlite3_shutdown(); return (retcode); }
int nftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int nfds, int ftwflags) { /* LINTED */ char * const paths[2] = { __UNCONST(path), NULL }; struct FTW f; FTSENT *cur; FTS *ftsp; int ftsflags, fnflag, error, postorder, sverrno; /* XXX - nfds is currently unused */ if (nfds < 1 || nfds > OPEN_MAX) { errno = EINVAL; return (-1); } ftsflags = FTS_COMFOLLOW; if (!(ftwflags & FTW_CHDIR)) ftsflags |= FTS_NOCHDIR; if (ftwflags & FTW_MOUNT) ftsflags |= FTS_XDEV; if (ftwflags & FTW_PHYS) ftsflags |= FTS_PHYSICAL; postorder = (ftwflags & FTW_DEPTH) != 0; ftsp = fts_open(paths, ftsflags, NULL); if (ftsp == NULL) return (-1); error = 0; while ((cur = fts_read(ftsp)) != NULL) { switch (cur->fts_info) { case FTS_D: if (postorder) continue; fnflag = FTW_D; break; case FTS_DNR: fnflag = FTW_DNR; break; case FTS_DP: if (!postorder) continue; fnflag = FTW_DP; break; case FTS_F: case FTS_DEFAULT: fnflag = FTW_F; break; case FTS_NS: case FTS_NSOK: fnflag = FTW_NS; break; case FTS_SL: fnflag = FTW_SL; break; case FTS_SLNONE: fnflag = FTW_SLN; break; case FTS_DC: errno = ELOOP; /* FALLTHROUGH */ default: error = -1; goto done; } f.base = cur->fts_pathlen - cur->fts_namelen; f.level = cur->fts_level; error = fn(cur->fts_path, cur->fts_statp, fnflag, &f); if (error != 0) break; } done: sverrno = errno; (void) fts_close(ftsp); errno = sverrno; return (error); }
void parseDomainFile(AtomPtr file, DomainPtr **domains_return, regex_t **regex_return) { struct stat ss; regex_t *regex; int rc; if(*domains_return) { DomainPtr *domain = *domains_return; while(*domain) { free(*domain); domain++; } free(*domains_return); *domains_return = NULL; } if(*regex_return) { regfree(*regex_return); *regex_return = NULL; } if(!file || file->length == 0) return; domains = malloc(64 * sizeof(DomainPtr)); if(domains == NULL) { do_log(L_ERROR, "Couldn't allocate domain list.\n"); return; } dlen = 0; dsize = 64; regexbuf = malloc(512); if(regexbuf == NULL) { do_log(L_ERROR, "Couldn't allocate regex.\n"); free(domains); return; } rlen = 0; rsize = 512; rc = stat(file->string, &ss); if(rc < 0) { if(errno != ENOENT) do_log_error(L_WARN, errno, "Couldn't stat file %s", file->string); } else { if(!S_ISDIR(ss.st_mode)) readDomainFile(file->string); else { char *fts_argv[2]; FTS *fts; FTSENT *fe; fts_argv[0] = file->string; fts_argv[1] = NULL; fts = fts_open(fts_argv, FTS_LOGICAL, NULL); if(fts) { while(1) { fe = fts_read(fts); if(!fe) break; if(fe->fts_info != FTS_D && fe->fts_info != FTS_DP && fe->fts_info != FTS_DC && fe->fts_info != FTS_DNR) readDomainFile(fe->fts_accpath); } fts_close(fts); } else { do_log_error(L_ERROR, errno, "Couldn't scan directory %s", file->string); } } } if(dlen > 0) { domains[dlen] = NULL; } else { free(domains); domains = NULL; } if(rlen > 0) { regex = malloc(sizeof(regex_t)); rc = regcomp(regex, regexbuf, REG_EXTENDED | REG_NOSUB); if(rc != 0) { char errbuf[100]; regerror(rc, regex, errbuf, 100); do_log(L_ERROR, "Couldn't compile regex: %s.\n", errbuf); free(regex); regex = NULL; } } else { regex = NULL; } free(regexbuf); *domains_return = domains; *regex_return = regex; return; }
int main(int argc, char **argv) { char *log_file = NULL; char *queue_dir = NULL; char *list[2]; int num_to_send = DEFAULT_NUM, chunk; int fd; FTS *fts; FTSENT *ftsent; int piece, npieces; char filename[PATH_MAX]; err_prog_name(argv[0]); OPTIONS("[-l log] [-n num] queuedir") NUMBER('n', num_to_send) STRING('l', log_file) ENDOPTS if (argc != 2) usage(); if (log_file) err_set_log(log_file); queue_dir = argv[1]; list[0] = queue_dir; list[1] = NULL; fts = fts_open(list, FTS_PHYSICAL|FTS_COMFOLLOW, fts_sort); if (fts == NULL) { err("fts failed on `%s'", queue_dir); exit(1); } ftsent = fts_read(fts); if (ftsent == NULL || ftsent->fts_info != FTS_D) { err("not a directory: %s", queue_dir); exit(1); } ftsent = fts_children(fts, 0); if (ftsent == NULL && errno) { err("*ftschildren failed"); exit(1); } for (chunk = 1; ftsent != NULL; ftsent = ftsent->fts_link) { /* * Skip non-files and ctm_smail tmp files (ones starting with `.') */ if (ftsent->fts_info != FTS_F || ftsent->fts_name[0] == '.') continue; sprintf(filename, "%s/%s", queue_dir, ftsent->fts_name); fd = open(filename, O_RDONLY); if (fd < 0) { err("*open: %s", filename); exit(1); } if (run_sendmail(fd)) exit(1); close(fd); if (unlink(filename) < 0) { err("*unlink: %s", filename); exit(1); } /* * Deduce the delta, piece number, and number of pieces from * the name of the file in the queue. Ideally, we should be * able to get the mail alias name too. * * NOTE: This depends intimately on the queue name used in ctm_smail. */ npieces = atoi(&ftsent->fts_name[ftsent->fts_namelen-3]); piece = atoi(&ftsent->fts_name[ftsent->fts_namelen-7]); err("%.*s %d/%d sent", ftsent->fts_namelen-8, ftsent->fts_name, piece, npieces); if (chunk++ == num_to_send) break; } fts_close(fts); return(0); }
int main(int argc, char **argv) { FTS *ftsp; FTSENT *p; int Hflag, Lflag, Rflag, fflag, hflag, vflag, xflag; int ch, fts_options, rval; char *cp; ischown = (strcmp(basename(argv[0]), "chown") == 0); Hflag = Lflag = Rflag = fflag = hflag = vflag = xflag = 0; while ((ch = getopt(argc, argv, "HLPRfhvx")) != -1) switch (ch) { case 'H': Hflag = 1; Lflag = 0; break; case 'L': Lflag = 1; Hflag = 0; break; case 'P': Hflag = Lflag = 0; break; case 'R': Rflag = 1; break; case 'f': fflag = 1; break; case 'h': hflag = 1; break; case 'v': vflag++; break; case 'x': xflag = 1; break; case '?': default: usage(); } argv += optind; argc -= optind; if (argc < 2) usage(); (void)signal(SIGINFO, siginfo_handler); if (Rflag) { if (hflag && (Hflag || Lflag)) errx(1, "the -R%c and -h options may not be " "specified together", Hflag ? 'H' : 'L'); if (Lflag) { fts_options = FTS_LOGICAL; } else { fts_options = FTS_PHYSICAL; if (Hflag) { fts_options |= FTS_COMFOLLOW; } } } else if (hflag) { fts_options = FTS_PHYSICAL; } else { fts_options = FTS_LOGICAL; } if (xflag) fts_options |= FTS_XDEV; uid = (uid_t)-1; gid = (gid_t)-1; if (ischown) { if ((cp = strchr(*argv, ':')) != NULL) { *cp++ = '\0'; a_gid(cp); } #ifdef SUPPORT_DOT else if ((cp = strchr(*argv, '.')) != NULL) { warnx("separation of user and group with a period is deprecated"); *cp++ = '\0'; a_gid(cp); } #endif a_uid(*argv); } else a_gid(*argv); if ((ftsp = fts_open(++argv, fts_options, NULL)) == NULL) err(1, NULL); for (rval = 0; (p = fts_read(ftsp)) != NULL;) { int atflag; if ((fts_options & FTS_LOGICAL) || ((fts_options & FTS_COMFOLLOW) && p->fts_level == FTS_ROOTLEVEL)) atflag = 0; else atflag = AT_SYMLINK_NOFOLLOW; switch (p->fts_info) { case FTS_D: /* Change it at FTS_DP. */ if (!Rflag) fts_set(ftsp, p, FTS_SKIP); continue; case FTS_DNR: /* Warn, chown. */ warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; break; case FTS_ERR: /* Warn, continue. */ case FTS_NS: warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; continue; default: break; } if (siginfo) { print_info(p, 2); siginfo = 0; } if ((uid == (uid_t)-1 || uid == p->fts_statp->st_uid) && (gid == (gid_t)-1 || gid == p->fts_statp->st_gid)) continue; if (fchownat(AT_FDCWD, p->fts_accpath, uid, gid, atflag) == -1 && !fflag) { chownerr(p->fts_path); rval = 1; } else if (vflag) print_info(p, vflag); } if (errno) err(1, "fts_read"); exit(rval); }
static int archive(const char *filename, int arglen, char *args[]) { xar_t x; FTS *fts; FTSENT *ent; int flags; struct lnode *i; const char *default_compression; x = xar_open(filename, WRITE); if( !x ) { fprintf(stderr, "Error creating archive %s\n", filename); exit(1); } if( Toccksum ) xar_opt_set(x, XAR_OPT_TOCCKSUM, Toccksum); if( Compression ) xar_opt_set(x, XAR_OPT_COMPRESSION, Compression); if( CompressionArg ) xar_opt_set(x, XAR_OPT_COMPRESSIONARG, CompressionArg); if( Coalesce ) xar_opt_set(x, XAR_OPT_COALESCE, "true"); if( LinkSame ) xar_opt_set(x, XAR_OPT_LINKSAME, "true"); if ( Rsize != NULL ) xar_opt_set(x, XAR_OPT_RSIZE, Rsize); xar_register_errhandler(x, err_callback, NULL); for( i = PropInclude; i; i=i->next ) { xar_opt_set(x, XAR_OPT_PROPINCLUDE, i->str); } for( i = PropExclude; i; i=i->next ) { xar_opt_set(x, XAR_OPT_PROPEXCLUDE, i->str); } if( Subdoc ) add_subdoc(x); if( Perms == SYMBOLIC ) { xar_opt_set(x, XAR_OPT_OWNERSHIP, XAR_OPT_VAL_SYMBOLIC); } if( Perms == NUMERIC ) { xar_opt_set(x, XAR_OPT_OWNERSHIP, XAR_OPT_VAL_NUMERIC); } default_compression = strdup(xar_opt_get(x, XAR_OPT_COMPRESSION)); if( !default_compression ) default_compression = strdup(XAR_OPT_VAL_GZIP); flags = FTS_PHYSICAL|FTS_NOSTAT|FTS_NOCHDIR; if( Local ) flags |= FTS_XDEV; fts = fts_open(args, flags, NULL); if( !fts ) { fprintf(stderr, "Error traversing file tree\n"); exit(1); } while( (ent = fts_read(fts)) ) { xar_file_t f; int exclude_match = 1; int nocompress_match = 1; if( ent->fts_info == FTS_DP ) continue; if( strcmp(ent->fts_path, "/") == 0 ) continue; if( strcmp(ent->fts_path, ".") == 0 ) continue; for( i = Exclude; i; i=i->next ) { exclude_match = regexec(&i->reg, ent->fts_path, 0, NULL, 0); if( !exclude_match ) break; } if( !exclude_match ) { if( Verbose ) printf("Excluding %s\n", ent->fts_path); continue; } for( i = NoCompress; i; i=i->next ) { nocompress_match = regexec(&i->reg, ent->fts_path, 0, NULL, 0); if( !nocompress_match ) { xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE); break; } } f = xar_add(x, ent->fts_path); if( !f ) { fprintf(stderr, "Error adding file %s\n", ent->fts_path); } else { print_file(x, f); } if( !nocompress_match ) xar_opt_set(x, XAR_OPT_COMPRESSION, default_compression); } fts_close(fts); if( xar_close(x) != 0 ) { fprintf(stderr, "Error creating the archive\n"); if( !Err ) Err = 42; } free((char *)default_compression); for( i = Exclude; i; ) { struct lnode *tmp; regfree(&i->reg); tmp = i; i = i->next; free(tmp); } for( i = NoCompress; i; ) { struct lnode *tmp; regfree(&i->reg); tmp = i; i = i->next; free(tmp); } return Err; }
static int rm_tree(char **argv) { FTS *fts; FTSENT *p; int needstat; int flags; int rval; /* * Check up front before anything is deleted. This will not catch * everything, but we'll check the individual items later. */ int i; for (i = 0; argv[i]; i++) { if (kBuildProtectionEnforce(&g_ProtData, KBUILDPROTECTIONTYPE_RECURSIVE, argv[i])) { return 1; } } /* * Remove a file hierarchy. If forcing removal (-f), or interactive * (-i) or can't ask anyway (stdin_ok), don't stat the file. */ needstat = !uid || (!fflag && !iflag && stdin_ok); /* * If the -i option is specified, the user can skip on the pre-order * visit. The fts_number field flags skipped directories. */ #define SKIPPED 1 flags = FTS_PHYSICAL; if (!needstat) flags |= FTS_NOSTAT; #ifdef FTS_WHITEOUT if (Wflag) flags |= FTS_WHITEOUT; #endif if (!(fts = fts_open(argv, flags, NULL))) { return err(1, "fts_open"); } while ((p = fts_read(fts)) != NULL) { const char *operation = "chflags"; switch (p->fts_info) { case FTS_DNR: if (!fflag || p->fts_errno != ENOENT) { fprintf(stderr, "fts: %s: %s: %s" CUR_LINE() "\n", argv0, p->fts_path, strerror(p->fts_errno)); eval = 1; } continue; case FTS_ERR: fts_close(fts); return errx(1, "fts: %s: %s " CUR_LINE(), p->fts_path, strerror(p->fts_errno)); case FTS_NS: /* * Assume that since fts_read() couldn't stat the * file, it can't be unlinked. */ if (!needstat) break; if (!fflag || p->fts_errno != ENOENT) { fprintf(stderr, "fts: %s: %s: %s " CUR_LINE() "\n", argv0, p->fts_path, strerror(p->fts_errno)); eval = 1; } continue; case FTS_D: /* Pre-order: give user chance to skip. */ if (!fflag && !check(p->fts_path, p->fts_accpath, p->fts_statp)) { (void)fts_set(fts, p, FTS_SKIP); p->fts_number = SKIPPED; } #ifdef UF_APPEND else if (!uid && (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && chflags(p->fts_accpath, p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) goto err; #endif continue; case FTS_DP: /* Post-order: see if user skipped. */ if (p->fts_number == SKIPPED) continue; break; default: if (!fflag && !check(p->fts_path, p->fts_accpath, p->fts_statp)) continue; } /* * Protect against deleting root files and directories. */ if (kBuildProtectionEnforce(&g_ProtData, KBUILDPROTECTIONTYPE_RECURSIVE, p->fts_accpath)) { fts_close(fts); return 1; } rval = 0; #ifdef UF_APPEND if (!uid && (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE))) rval = chflags(p->fts_accpath, p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); #endif if (rval == 0) { /* * If we can't read or search the directory, may still be * able to remove it. Don't print out the un{read,search}able * message unless the remove fails. */ switch (p->fts_info) { case FTS_DP: case FTS_DNR: rval = rmdir(p->fts_accpath); if (rval == 0 || (fflag && errno == ENOENT)) { if (rval == 0 && vflag) (void)printf("%s\n", p->fts_path); continue; } operation = "mkdir"; break; #ifdef FTS_W case FTS_W: rval = undelete(p->fts_accpath); if (rval == 0 && (fflag && errno == ENOENT)) { if (vflag) (void)printf("%s\n", p->fts_path); continue; } operation = "undelete"; break; #endif case FTS_NS: /* * Assume that since fts_read() couldn't stat * the file, it can't be unlinked. */ if (fflag) continue; /* FALLTHROUGH */ default: if (Pflag) if (!rm_overwrite(p->fts_accpath, NULL)) continue; rval = unlink(p->fts_accpath); #ifdef _MSC_VER if (rval != 0) { chmod(p->fts_accpath, 0777); rval = unlink(p->fts_accpath); } #endif if (rval == 0 || (fflag && errno == ENOENT)) { if (rval == 0 && vflag) (void)printf("%s\n", p->fts_path); continue; } operation = "unlink"; break; } } #ifdef UF_APPEND err: #endif fprintf(stderr, "%s: %s: %s: %s " CUR_LINE() "\n", operation, argv0, p->fts_path, strerror(errno)); eval = 1; } if (errno) { fprintf(stderr, "%s: fts_read: %s " CUR_LINE() "\n", argv0, strerror(errno)); eval = 1; } fts_close(fts); return eval; }
static int copy(char *argv[], enum op type, int fts_options) { struct stat to_stat; FTS *ftsp; FTSENT *curr; int base = 0, dne, badcp, rval; size_t nlen; char *p, *target_mid; mode_t mask, mode; /* * Keep an inverted copy of the umask, for use in correcting * permissions on created directories when not using -p. */ mask = ~umask(0777); umask(~mask); if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL) err(1, "fts_open"); for (badcp = rval = 0; (curr = fts_read(ftsp)) != NULL; badcp = 0) { switch (curr->fts_info) { case FTS_NS: case FTS_DNR: case FTS_ERR: warnx("%s: %s", curr->fts_path, strerror(curr->fts_errno)); badcp = rval = 1; continue; case FTS_DC: /* Warn, continue. */ warnx("%s: directory causes a cycle", curr->fts_path); badcp = rval = 1; continue; default: ; } /* * If we are in case (2) or (3) above, we need to append the * source name to the target name. */ if (type != FILE_TO_FILE) { /* * Need to remember the roots of traversals to create * correct pathnames. If there's a directory being * copied to a non-existent directory, e.g. * cp -R a/dir noexist * the resulting path name should be noexist/foo, not * noexist/dir/foo (where foo is a file in dir), which * is the case where the target exists. * * Also, check for "..". This is for correct path * concatenation for paths ending in "..", e.g. * cp -R .. /tmp * Paths ending in ".." are changed to ".". This is * tricky, but seems the easiest way to fix the problem. * * XXX * Since the first level MUST be FTS_ROOTLEVEL, base * is always initialized. */ if (curr->fts_level == FTS_ROOTLEVEL) { if (type != DIR_TO_DNE) { p = strrchr(curr->fts_path, '/'); base = (p == NULL) ? 0 : (int)(p - curr->fts_path + 1); if (!strcmp(&curr->fts_path[base], "..")) base += 1; } else base = curr->fts_pathlen; } p = &curr->fts_path[base]; nlen = curr->fts_pathlen - base; target_mid = to.target_end; if (*p != '/' && target_mid[-1] != '/') *target_mid++ = '/'; *target_mid = 0; if (target_mid - to.p_path + nlen >= PATH_MAX) { warnx("%s%s: name too long (not copied)", to.p_path, p); badcp = rval = 1; continue; } (void)strncat(target_mid, p, nlen); to.p_end = target_mid + nlen; *to.p_end = 0; STRIP_TRAILING_SLASH(to); } if (curr->fts_info == FTS_DP) { /* * We are nearly finished with this directory. If we * didn't actually copy it, or otherwise don't need to * change its attributes, then we are done. */ if (!curr->fts_number) continue; /* * If -p is in effect, set all the attributes. * Otherwise, set the correct permissions, limited * by the umask. Optimise by avoiding a chmod() * if possible (which is usually the case if we * made the directory). Note that mkdir() does not * honour setuid, setgid and sticky bits, but we * normally want to preserve them on directories. */ if (pflag) { if (setfile(curr->fts_statp, -1)) rval = 1; if (preserve_dir_acls(curr->fts_statp, curr->fts_accpath, to.p_path) != 0) rval = 1; } else { mode = curr->fts_statp->st_mode; if ((mode & (S_ISUID | S_ISGID | S_ISTXT)) || ((mode | S_IRWXU) & mask) != (mode & mask)) if (chmod(to.p_path, mode & mask) != 0){ warn("chmod: %s", to.p_path); rval = 1; } } continue; } /* Not an error but need to remember it happened */ if (stat(to.p_path, &to_stat) == -1) dne = 1; else { if (to_stat.st_dev == curr->fts_statp->st_dev && to_stat.st_ino == curr->fts_statp->st_ino) { warnx("%s and %s are identical (not copied).", to.p_path, curr->fts_path); badcp = rval = 1; if (S_ISDIR(curr->fts_statp->st_mode)) (void)fts_set(ftsp, curr, FTS_SKIP); continue; } if (!S_ISDIR(curr->fts_statp->st_mode) && S_ISDIR(to_stat.st_mode)) { warnx("cannot overwrite directory %s with " "non-directory %s", to.p_path, curr->fts_path); badcp = rval = 1; continue; } dne = 0; } switch (curr->fts_statp->st_mode & S_IFMT) { case S_IFLNK: /* Catch special case of a non-dangling symlink */ if ((fts_options & FTS_LOGICAL) || ((fts_options & FTS_COMFOLLOW) && curr->fts_level == 0)) { if (copy_file(curr, dne)) badcp = rval = 1; } else { if (copy_link(curr, !dne)) badcp = rval = 1; } break; case S_IFDIR: if (!Rflag) { warnx("%s is a directory (not copied).", curr->fts_path); (void)fts_set(ftsp, curr, FTS_SKIP); badcp = rval = 1; break; } /* * If the directory doesn't exist, create the new * one with the from file mode plus owner RWX bits, * modified by the umask. Trade-off between being * able to write the directory (if from directory is * 555) and not causing a permissions race. If the * umask blocks owner writes, we fail.. */ if (dne) { if (mkdir(to.p_path, curr->fts_statp->st_mode | S_IRWXU) < 0) err(1, "%s", to.p_path); } else if (!S_ISDIR(to_stat.st_mode)) { errno = ENOTDIR; err(1, "%s", to.p_path); } /* * Arrange to correct directory attributes later * (in the post-order phase) if this is a new * directory, or if the -p flag is in effect. */ curr->fts_number = pflag || dne; break; case S_IFBLK: case S_IFCHR: if (Rflag) { if (copy_special(curr->fts_statp, !dne)) badcp = rval = 1; } else { if (copy_file(curr, dne)) badcp = rval = 1; } break; case S_IFSOCK: warnx("%s is a socket (not copied).", curr->fts_path); break; case S_IFIFO: if (Rflag) { if (copy_fifo(curr->fts_statp, !dne)) badcp = rval = 1; } else { if (copy_file(curr, dne)) badcp = rval = 1; } break; default: if (copy_file(curr, dne)) badcp = rval = 1; break; } if (vflag && !badcp) (void)printf("%s -> %s\n", curr->fts_path, to.p_path); } if (errno) err(1, "fts_read"); fts_close(ftsp); return (rval); }
int main(int argc, char *argv[]) { FTS *fts; FTSENT *p; off_t savednumber, curblocks; off_t threshold, threshold_sign; int ftsoptions; int depth; int Hflag, Lflag, aflag, sflag, dflag, cflag; int hflag, lflag, ch, notused, rval; char **save; static char dot[] = "."; setlocale(LC_ALL, ""); Hflag = Lflag = aflag = sflag = dflag = cflag = hflag = lflag = Aflag = 0; save = argv; ftsoptions = FTS_PHYSICAL; savednumber = 0; threshold = 0; threshold_sign = 1; cblocksize = DEV_BSIZE; blocksize = 0; depth = INT_MAX; SLIST_INIT(&ignores); while ((ch = getopt(argc, argv, "AB:HI:LPasd:chklmnrt:x")) != -1) switch (ch) { case 'A': Aflag = 1; break; case 'B': errno = 0; cblocksize = atoi(optarg); if (errno == ERANGE || cblocksize <= 0) { warnx("invalid argument to option B: %s", optarg); usage(); } break; case 'H': Hflag = 1; Lflag = 0; break; case 'I': ignoreadd(optarg); break; case 'L': Lflag = 1; Hflag = 0; break; case 'P': Hflag = Lflag = 0; break; case 'a': aflag = 1; break; case 's': sflag = 1; break; case 'd': dflag = 1; errno = 0; depth = atoi(optarg); if (errno == ERANGE || depth < 0) { warnx("invalid argument to option d: %s", optarg); usage(); } break; case 'c': cflag = 1; break; case 'h': hflag = 1; break; case 'k': hflag = 0; blocksize = 1024; break; case 'l': lflag = 1; break; case 'm': hflag = 0; blocksize = 1048576; break; case 'n': nodumpflag = 1; break; case 'r': /* Compatibility. */ break; case 't' : if (expand_number(optarg, &threshold) != 0 || threshold == 0) { warnx("invalid threshold: %s", optarg); usage(); } else if (threshold < 0) threshold_sign = -1; break; case 'x': ftsoptions |= FTS_XDEV; break; case '?': default: usage(); /* NOTREACHED */ } argc -= optind; argv += optind; /* * XXX * Because of the way that fts(3) works, logical walks will not count * the blocks actually used by symbolic links. We rationalize this by * noting that users computing logical sizes are likely to do logical * copies, so not counting the links is correct. The real reason is * that we'd have to re-implement the kernel's symbolic link traversing * algorithm to get this right. If, for example, you have relative * symbolic links referencing other relative symbolic links, it gets * very nasty, very fast. The bottom line is that it's documented in * the man page, so it's a feature. */ if (Hflag) ftsoptions |= FTS_COMFOLLOW; if (Lflag) { ftsoptions &= ~FTS_PHYSICAL; ftsoptions |= FTS_LOGICAL; } if (!Aflag && (cblocksize % DEV_BSIZE) != 0) cblocksize = howmany(cblocksize, DEV_BSIZE) * DEV_BSIZE; if (aflag + dflag + sflag > 1) usage(); if (sflag) depth = 0; if (!*argv) { argv = save; argv[0] = dot; argv[1] = NULL; } if (blocksize == 0) (void)getbsize(¬used, &blocksize); if (!Aflag) { cblocksize /= DEV_BSIZE; blocksize /= DEV_BSIZE; } if (threshold != 0) threshold = howmany(threshold / DEV_BSIZE * cblocksize, blocksize); rval = 0; (void)signal(SIGINFO, siginfo); if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL) err(1, "fts_open"); while ((p = fts_read(fts)) != NULL) { switch (p->fts_info) { case FTS_D: /* Ignore. */ if (ignorep(p)) fts_set(fts, p, FTS_SKIP); break; case FTS_DP: if (ignorep(p)) break; curblocks = Aflag ? howmany(p->fts_statp->st_size, cblocksize) : howmany(p->fts_statp->st_blocks, cblocksize); p->fts_parent->fts_bignum += p->fts_bignum += curblocks; if (p->fts_level <= depth && threshold <= threshold_sign * howmany(p->fts_bignum * cblocksize, blocksize)) { if (hflag) { prthumanval(p->fts_bignum); (void)printf("\t%s\n", p->fts_path); } else { (void)printf("%jd\t%s\n", (intmax_t)howmany(p->fts_bignum * cblocksize, blocksize), p->fts_path); } } if (info) { info = 0; (void)printf("\t%s\n", p->fts_path); } break; case FTS_DC: /* Ignore. */ break; case FTS_DNR: /* Warn, continue. */ case FTS_ERR: case FTS_NS: warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; break; default: if (ignorep(p)) break; if (lflag == 0 && p->fts_statp->st_nlink > 1 && linkchk(p)) break; curblocks = Aflag ? howmany(p->fts_statp->st_size, cblocksize) : howmany(p->fts_statp->st_blocks, cblocksize); if (aflag || p->fts_level == 0) { if (hflag) { prthumanval(curblocks); (void)printf("\t%s\n", p->fts_path); } else { (void)printf("%jd\t%s\n", (intmax_t)howmany(curblocks * cblocksize, blocksize), p->fts_path); } } p->fts_parent->fts_bignum += curblocks; } savednumber = p->fts_parent->fts_bignum; } if (errno) err(1, "fts_read"); if (cflag) { if (hflag) { prthumanval(savednumber); (void)printf("\ttotal\n"); } else { (void)printf("%jd\ttotal\n", (intmax_t)howmany( savednumber * cblocksize, blocksize)); } } ignoreclean(); exit(rval); }
/* * Traverse() walks the logical directory structure specified by the argv list * in the order specified by the mastercmp() comparison function. During the * traversal it passes linked lists of structures to display() which represent * a superset (may be exact set) of the files to be displayed. */ static void traverse(int argc, char *argv[], int options) { FTS *ftsp; FTSENT *p, *chp; int ch_options, saved_errno; if ((ftsp = fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) err(1, NULL); display(NULL, fts_children(ftsp, 0)); if (f_listdir) return; /* * If not recursing down this tree and don't need stat info, just get * the names. */ ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0; while ((p = fts_read(ftsp)) != NULL) switch (p->fts_info) { case FTS_D: if (p->fts_name[0] == '.' && p->fts_level != FTS_ROOTLEVEL && !f_listdot) break; /* * If already output something, put out a newline as * a separator. If multiple arguments, precede each * directory with its name. */ if (output) (void)printf("\n%s:\n", p->fts_path); else if (argc > 1) { (void)printf("%s:\n", p->fts_path); output = 1; } chp = fts_children(ftsp, ch_options); saved_errno = errno; display(p, chp); /* * On fts_children() returning error do recurse to see * the error. */ if (!f_recursive && !(chp == NULL && saved_errno != 0)) (void)fts_set(ftsp, p, FTS_SKIP); break; case FTS_DC: warnx("%s: directory causes a cycle", p->fts_name); break; case FTS_DNR: case FTS_ERR: warnx("%s: %s", p->fts_name[0] == '\0' ? p->fts_path : p->fts_name, strerror(p->fts_errno)); rval = 1; break; } if (errno) err(1, "fts_read"); fts_close(ftsp); }
void __do_dremove(IN tt_io_ev_t *io_ev) { __dremove_t *dremove = (__dremove_t *)io_ev; tt_char_t *path[2] = {(tt_char_t *)dremove->path, NULL}; FTS *fts; FTSENT *ftse; retry1: fts = fts_open(path, FTS_NOCHDIR | FTS_NOSTAT | FTS_PHYSICAL, NULL); if (fts == NULL) { if (errno == EINTR) { goto retry1; } else { TT_ERROR_NTV("fail to open fts: %s", path); dremove->result = TT_FAIL; return; } } while (1) { retry2: ftse = fts_read(fts); if (ftse == NULL) { if (errno == 0) { break; } else if (errno == EINTR) { goto retry2; } else { TT_ERROR_NTV("fail to read fts"); __RETRY_IF_EINTR(fts_close(fts) != 0); dremove->result = TT_FAIL; return; } } switch (ftse->fts_info) { case FTS_DEFAULT: case FTS_F: case FTS_NSOK: case FTS_SL: case FTS_SLNONE: { // remove file if (unlink(ftse->fts_accpath) != 0) { TT_ERROR_NTV("fail to remove file[%s]", ftse->fts_accpath); __RETRY_IF_EINTR(fts_close(fts) != 0); dremove->result = TT_FAIL; return; } } break; case FTS_DP: { // can remove directory now if (rmdir(ftse->fts_accpath) != 0) { TT_ERROR_NTV("fail to remove directory[%s]", ftse->fts_accpath); __RETRY_IF_EINTR(fts_close(fts) != 0); dremove->result = TT_FAIL; return; } } break; case FTS_D: { // something to ignore } break; default: { // something unexpected TT_ERROR("expected fts info: %d", ftse->fts_info); __RETRY_IF_EINTR(fts_close(fts) != 0); dremove->result = TT_FAIL; return; } break; } } __RETRY_IF_EINTR(fts_close(fts) != 0); dremove->result = TT_SUCCESS; }
static int ftree_arg(void) { char *pt; /* * close off the current file tree */ if (ftsp != NULL) { (void)fts_close(ftsp); ftsp = NULL; } /* * keep looping until we get a valid file tree to process. Stop when we * reach the end of the list (or get an eof on stdin) */ for(;;) { if (fthead == NULL) { /* * the user didn't supply any args, get the file trees * to process from stdin; */ if (fgets(farray[0], PAXPATHLEN+1, stdin) == NULL) return(-1); if ((pt = strchr(farray[0], '\n')) != NULL) *pt = '\0'; } else { /* * the user supplied the file args as arguments to pax */ if (ftcur == NULL) ftcur = fthead; else if ((ftcur = ftcur->fow) == NULL) return(-1); if (ftcur->chflg) { /* First fchdir() back... */ if (fchdir(cwdfd) < 0) { syswarn(1, errno, "Can't fchdir to starting directory"); return(-1); } if (chdir(ftcur->fname) < 0) { syswarn(1, errno, "Can't chdir to %s", ftcur->fname); return(-1); } continue; } else farray[0] = ftcur->fname; } /* * Watch it, fts wants the file arg stored in an array of char * ptrs, with the last one a null. We use a two element array * and set farray[0] to point at the buffer with the file name * in it. We cannot pass all the file args to fts at one shot * as we need to keep a handle on which file arg generates what * files (the -n and -d flags need this). If the open is * successful, return a 0. */ if ((ftsp = fts_open(farray, ftsopts, NULL)) != NULL) break; } return(0); }
int main(int argc, char **argv) { FTS *ftsp; FTSENT *p; int Hflag, Lflag, Pflag, Rflag, fflag, hflag, vflag; int ch, fts_options, rval; char *cp; int unix2003_compat = 0; int symlink_found = 0; if (argc < 1) usage(); cp = strrchr(argv[0], '/'); cp = (cp != NULL) ? cp + 1 : argv[0]; ischown = (strcmp(cp, "chown") == 0); Hflag = Lflag = Pflag = Rflag = fflag = hflag = vflag = 0; while ((ch = getopt(argc, argv, "HLPRfhv")) != -1) switch (ch) { case 'H': Hflag = 1; Lflag = Pflag = 0; break; case 'L': Lflag = 1; Hflag = Pflag = 0; break; case 'P': Pflag = 1; Hflag = Lflag = 0; break; case 'R': Rflag = 1; break; case 'f': fflag = 1; break; case 'h': hflag = 1; break; case 'v': vflag = 1; break; case '?': default: usage(); } argv += optind; argc -= optind; if (argc < 2) usage(); if (!Rflag && (Hflag || Lflag || Pflag)) warnx("options -H, -L, -P only useful with -R"); if (Rflag) { fts_options = FTS_PHYSICAL; if (hflag && (Hflag || Lflag)) errx(1, "the -R%c and -h options may not be " "specified together", Hflag ? 'H' : 'L'); if (Hflag) fts_options |= FTS_COMFOLLOW; else if (Lflag) { fts_options &= ~FTS_PHYSICAL; fts_options |= FTS_LOGICAL; } } else fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL; uid = (uid_t)-1; gid = (gid_t)-1; if (ischown) { unix2003_compat = COMPAT_MODE("bin/chown", "Unix2003"); if ((cp = strchr(*argv, ':')) != NULL) { *cp++ = '\0'; a_gid(cp); } #ifdef SUPPORT_DOT else if ((cp = strchr(*argv, '.')) != NULL) { warnx("separation of user and group with a period is deprecated"); *cp++ = '\0'; a_gid(cp); } #endif a_uid(*argv); } else { unix2003_compat = COMPAT_MODE("bin/chgrp", "Unix2003"); a_gid(*argv); } if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) err(1, NULL); for (rval = 0; (p = fts_read(ftsp)) != NULL;) { symlink_found = 0; switch (p->fts_info) { case FTS_D: /* Change it at FTS_DP. */ if (!Rflag) fts_set(ftsp, p, FTS_SKIP); continue; case FTS_DNR: /* Warn, chown. */ warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; break; case FTS_ERR: /* Warn, continue. */ case FTS_NS: warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; continue; case FTS_SL: case FTS_SLNONE: /* * The only symlinks that end up here are ones that * don't point to anything and ones that we found * doing a physical walk. */ if (hflag) break; else { symlink_found = 1; if (unix2003_compat) { if (Hflag || Lflag) { /* -H or -L was specified */ if (p->fts_errno) { warnx("%s: %s", p->fts_name, strerror(p->fts_errno)); rval = 1; continue; } } break; /* Otherwise symlinks keep going */ } continue; } default: break; } if (unix2003_compat) { /* Can only avoid updating times if both uid and gid are -1 */ if ((uid == (uid_t)-1) && (gid == (gid_t)-1)) continue; } else { if ((uid == (uid_t)-1 || uid == p->fts_statp->st_uid) && (gid == (gid_t)-1 || gid == p->fts_statp->st_gid)) continue; } if (((hflag || symlink_found) ? lchown : chown)(p->fts_accpath, uid, gid) == -1) { if (!fflag) { chownerr(p->fts_path); rval = 1; } } else { if (vflag) printf("%s\n", p->fts_path); } } if (errno) err(1, "fts_read"); exit(rval); }
static FTS* _fts_open(char *path, int opts) { char *npath[2] = { path, NULL }; return fts_open(npath, opts, NULL); }
int main(int argc, char *argv[]) { static char *arg[2]; struct stat sb; FTS *ftsp; FTSENT *f; int rval, c, type; while ((c = getopt(argc, argv, "ad:in:rst:x")) != -1) switch (c) { case 'a': opt_all = 1; break; case 'd': opt_dir = optarg; break; case 'i': opt_ignore = 1; break; case 'n': opt_name = optarg; break; case 'r': opt_recurse = 1; break; case 's': opt_silent = 1; break; case 't': if ((opt_type = disttype(optarg)) == 0) { warnx("illegal argument to -t option"); usage(); } break; case 'x': opt_exist = 1; break; default: usage(); } argc -= optind; argv += optind; if (argc < 1) usage(); if (opt_dir) { if (stat(opt_dir, &sb)) err(2, "%s", opt_dir); if (!S_ISDIR(sb.st_mode)) errx(2, "%s: not a directory", opt_dir); } rval = 0; do { if (isstdin(*argv)) rval |= ckdist(*argv, opt_type); else if (stat(*argv, &sb)) rval |= fail(*argv, NULL); else if (S_ISREG(sb.st_mode)) rval |= ckdist(*argv, opt_type); else { arg[0] = *argv; if ((ftsp = fts_open(arg, FTS_LOGICAL, NULL)) == NULL) err(2, "fts_open"); while ((f = fts_read(ftsp)) != NULL) switch (f->fts_info) { case FTS_DC: rval = fail(f->fts_path, "Directory causes a cycle"); break; case FTS_DNR: case FTS_ERR: case FTS_NS: rval = fail(f->fts_path, sys_errlist[f->fts_errno]); break; case FTS_D: if (!opt_recurse && f->fts_level > FTS_ROOTLEVEL && fts_set(ftsp, f, FTS_SKIP)) err(2, "fts_set"); break; case FTS_F: if ((type = distfile(f->fts_name)) != 0 && (!opt_type || type == opt_type)) rval |= ckdist(f->fts_path, type); break; default: ; } if (errno) err(2, "fts_read"); if (fts_close(ftsp)) err(2, "fts_close"); } } while (*++argv); return rval; }
static void rm_tree(char **argv) { FTS *fts; FTSENT *p; int needstat; int flags; int rval; /* * Remove a file hierarchy. If forcing removal (-f), or interactive * (-i) or can't ask anyway (stdin_ok), don't stat the file. */ needstat = !uid || (!fflag && !iflag && stdin_ok); /* * If the -i option is specified, the user can skip on the pre-order * visit. The fts_number field flags skipped directories. */ #define SKIPPED 1 flags = FTS_PHYSICAL; if (!needstat) flags |= FTS_NOSTAT; if (Wflag) flags |= FTS_WHITEOUT; if (xflag) flags |= FTS_XDEV; if (!(fts = fts_open(argv, flags, NULL))) { if (fflag && errno == ENOENT) return; err(1, "fts_open"); } while ((p = fts_read(fts)) != NULL) { switch (p->fts_info) { case FTS_DNR: if (!fflag || p->fts_errno != ENOENT) { warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); eval = 1; } continue; case FTS_ERR: errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno)); case FTS_NS: /* * Assume that since fts_read() couldn't stat the * file, it can't be unlinked. */ if (!needstat) break; if (!fflag || p->fts_errno != ENOENT) { warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); eval = 1; } continue; case FTS_D: /* Pre-order: give user chance to skip. */ if (!fflag && !check(p->fts_path, p->fts_accpath, p->fts_statp)) { (void)fts_set(fts, p, FTS_SKIP); p->fts_number = SKIPPED; } else if (!uid && (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && lchflags(p->fts_accpath, p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) goto err; continue; case FTS_DP: /* Post-order: see if user skipped. */ if (p->fts_number == SKIPPED) continue; break; default: if (!fflag && !check(p->fts_path, p->fts_accpath, p->fts_statp)) continue; } rval = 0; if (!uid && (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE))) rval = lchflags(p->fts_accpath, p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); if (rval == 0) { /* * If we can't read or search the directory, may still be * able to remove it. Don't print out the un{read,search}able * message unless the remove fails. */ switch (p->fts_info) { case FTS_DP: case FTS_DNR: rval = rmdir(p->fts_accpath); if (rval == 0 || (fflag && errno == ENOENT)) { if (rval == 0 && vflag) (void)printf("%s\n", p->fts_path); if (rval == 0 && info) { info = 0; (void)printf("%s\n", p->fts_path); } continue; } break; case FTS_W: rval = undelete(p->fts_accpath); if (rval == 0 && (fflag && errno == ENOENT)) { if (vflag) (void)printf("%s\n", p->fts_path); if (info) { info = 0; (void)printf("%s\n", p->fts_path); } continue; } break; case FTS_NS: /* * Assume that since fts_read() couldn't stat * the file, it can't be unlinked. */ if (fflag) continue; /* FALLTHROUGH */ case FTS_F: case FTS_NSOK: if (Pflag) if (!rm_overwrite(p->fts_accpath, p->fts_info == FTS_NSOK ? NULL : p->fts_statp)) continue; /* FALLTHROUGH */ default: rval = unlink(p->fts_accpath); if (rval == 0 || (fflag && errno == ENOENT)) { if (rval == 0 && vflag) (void)printf("%s\n", p->fts_path); if (rval == 0 && info) { info = 0; (void)printf("%s\n", p->fts_path); } continue; } } } err: warn("%s", p->fts_path); eval = 1; } if (!fflag && errno) err(1, "fts_read"); fts_close(fts); }
int restorecon_main(int argc, char **argv) { int ch, recurse = 0, ftsflags = FTS_PHYSICAL; int i = 0; progname = argv[0]; do { ch = getopt(argc, argv, "nrRv"); if (ch == EOF) break; switch (ch) { case 'n': nochange = 1; break; case 'r': case 'R': recurse = 1; break; case 'v': verbose = 1; break; default: usage(); } } while (1); argc -= optind; argv += optind; if (!argc) usage(); sehandle = selinux_android_file_context_handle(); if (!sehandle) { fprintf(stderr, "Could not load file_contexts: %s\n", strerror(errno)); return -1; } if (recurse) { FTS *fts; FTSENT *ftsent; fts = fts_open(argv, ftsflags, NULL); if (!fts) { fprintf(stderr, "Could not traverse filesystems (first was %s): %s\n", argv[0], strerror(errno)); return -1; } while ((ftsent = fts_read(fts))) { switch (ftsent->fts_info) { case FTS_DP: break; case FTS_DNR: case FTS_ERR: case FTS_NS: fprintf(stderr, "Could not access %s: %s\n", ftsent->fts_path, strerror(errno)); fts_set(fts, ftsent, FTS_SKIP); break; default: if (restore(ftsent->fts_path, ftsent->fts_statp) < 0) fts_set(fts, ftsent, FTS_SKIP); break; } } } else { int i, rc; struct stat sb; for (i = 0; i < argc; i++) { rc = lstat(argv[i], &sb); if (rc < 0) { fprintf(stderr, "Could not stat %s: %s\n", argv[i], strerror(errno)); continue; } restore(argv[i], &sb); } } return 0; }
static int vwalk(void) { FTS *t; FTSENT *p; NODE *ep, *level; int specdepth, rval; char *argv[2]; char dot[] = "."; argv[0] = dot; argv[1] = NULL; if ((t = fts_open(argv, ftsoptions, nsort)) == NULL) err(1, "line %d: fts_open", lineno); level = root; specdepth = rval = 0; while ((p = fts_read(t))) { if (check_excludes(p->fts_name, p->fts_path)) { fts_set(t, p, FTS_SKIP); continue; } switch(p->fts_info) { case FTS_D: case FTS_SL: break; case FTS_DP: if (specdepth > p->fts_level) { for (level = level->parent; level->prev; level = level->prev); --specdepth; } continue; case FTS_DNR: case FTS_ERR: case FTS_NS: warnx("%s: %s", RP(p), strerror(p->fts_errno)); continue; default: if (dflag) continue; } if (specdepth != p->fts_level) goto extra; for (ep = level; ep; ep = ep->next) if ((ep->flags & F_MAGIC && !fnmatch(ep->name, p->fts_name, FNM_PATHNAME)) || !strcmp(ep->name, p->fts_name)) { ep->flags |= F_VISIT; if ((ep->flags & F_NOCHANGE) == 0 && compare(ep->name, ep, p)) rval = MISMATCHEXIT; if (ep->flags & F_IGN) (void)fts_set(t, p, FTS_SKIP); else if (ep->child && ep->type == F_DIR && p->fts_info == FTS_D) { level = ep->child; ++specdepth; } break; } if (ep) continue; extra: if (!eflag) { (void)printf("%s extra", RP(p)); if (rflag) { if ((S_ISDIR(p->fts_statp->st_mode) ? rmdir : unlink)(p->fts_accpath)) { (void)printf(", not removed: %s", strerror(errno)); } else (void)printf(", removed"); } (void)putchar('\n'); } (void)fts_set(t, p, FTS_SKIP); } (void)fts_close(t); if (sflag) warnx("%s checksum: %lu", fullpath, (unsigned long)crc_total); return (rval); }
static int ftree_arg(void) { /* * close off the current file tree */ if (ftsp != NULL) { (void)fts_close(ftsp); ftsp = NULL; } /* * keep looping until we get a valid file tree to process. Stop when we * reach the end of the list (or get an eof on stdin) */ for(;;) { if (fthead == NULL) { int i, c = EOF; /* * the user didn't supply any args, get the file trees * to process from stdin; */ for (i = 0; i < PAXPATHLEN + 2; i++) { c = getchar(); if (c == EOF) break; else if (c == sep) { if (i != 0) break; } else farray[0][i] = c; } if (i == 0) return -1; farray[0][i] = '\0'; } else { /* * the user supplied the file args as arguements to pax */ if (ftcur == NULL) ftcur = fthead; else if ((ftcur = ftcur->fow) == NULL) return -1; if (ftcur->refcnt < 0) { /* * chdir entry. * Change directory and retry loop. */ if (ar_dochdir(ftcur->fname)) return (-1); continue; } farray[0] = ftcur->fname; } /* * watch it, fts wants the file arg stored in a array of char * ptrs, with the last one a null. we use a two element array * and set farray[0] to point at the buffer with the file name * in it. We cannot pass all the file args to fts at one shot * as we need to keep a handle on which file arg generates what * files (the -n and -d flags need this). If the open is * successful, return a 0. */ if ((ftsp = fts_open(farray, ftsopts, NULL)) != NULL) break; } return 0; }
int copy(char *argv[], enum op type, int fts_options) { struct stat to_stat; FTS *ftsp; FTSENT *curr; int base, nlen, rval; char *p, *target_mid; base = 0; if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL) err(1, NULL); for (rval = 0; (curr = fts_read(ftsp)) != NULL;) { switch (curr->fts_info) { case FTS_NS: case FTS_DNR: case FTS_ERR: warnx("%s: %s", curr->fts_path, strerror(curr->fts_errno)); rval = 1; continue; case FTS_DC: warnx("%s: directory causes a cycle", curr->fts_path); rval = 1; continue; } /* * If we are in case (2) or (3) above, we need to append the * source name to the target name. */ if (type != FILE_TO_FILE) { /* * Need to remember the roots of traversals to create * correct pathnames. If there's a directory being * copied to a non-existent directory, e.g. * cp -R a/dir noexist * the resulting path name should be noexist/foo, not * noexist/dir/foo (where foo is a file in dir), which * is the case where the target exists. * * Also, check for "..". This is for correct path * concatenation for paths ending in "..", e.g. * cp -R .. /tmp * Paths ending in ".." are changed to ".". This is * tricky, but seems the easiest way to fix the problem. * * XXX * Since the first level MUST be FTS_ROOTLEVEL, base * is always initialized. */ if (curr->fts_level == FTS_ROOTLEVEL) { if (type != DIR_TO_DNE) { p = find_last_component(curr->fts_path); base = p - curr->fts_path; if (!strcmp(&curr->fts_path[base], "..")) base += 1; } else base = curr->fts_pathlen; } p = &curr->fts_path[base]; nlen = curr->fts_pathlen - base; target_mid = to.target_end; if (*p != '/' && target_mid[-1] != '/') *target_mid++ = '/'; *target_mid = '\0'; if (target_mid - to.p_path + nlen >= PATH_MAX) { warnx("%s%s: name too long (not copied)", to.p_path, p); rval = 1; continue; } (void)strncat(target_mid, p, nlen); to.p_end = target_mid + nlen; *to.p_end = '\0'; } /* Not an error but need to remember it happened */ if (stat(to.p_path, &to_stat) == -1) { if (curr->fts_info == FTS_DP) continue; /* * We use fts_pointer as a boolean to indicate that * we created this directory ourselves. We'll use * this later on via the fts_dne macro to decide * whether or not to set the directory mode during * the post-order pass. */ curr->fts_pointer = (void *)1; } else { /* * Set directory mode/user/times on the post-order * pass. We can't do this earlier because the mode * may not allow us write permission. Furthermore, * if we set the times during the pre-order pass, * they will get changed later when the directory * is populated. */ if (curr->fts_info == FTS_DP) { if (!S_ISDIR(to_stat.st_mode)) continue; /* * If not -p and directory didn't exist, set * it to be the same as the from directory, * unmodified by the umask; arguably wrong, * but it's been that way forever. */ if (pflag && setfile(curr->fts_statp, -1)) rval = 1; else if (fts_dne(curr)) (void)chmod(to.p_path, curr->fts_statp->st_mode); continue; } if (to_stat.st_dev == curr->fts_statp->st_dev && to_stat.st_ino == curr->fts_statp->st_ino) { warnx("%s and %s are identical (not copied).", to.p_path, curr->fts_path); rval = 1; if (S_ISDIR(curr->fts_statp->st_mode)) (void)fts_set(ftsp, curr, FTS_SKIP); continue; } if (!S_ISDIR(curr->fts_statp->st_mode) && S_ISDIR(to_stat.st_mode)) { warnx("cannot overwrite directory %s with non-directory %s", to.p_path, curr->fts_path); rval = 1; continue; } } switch (curr->fts_statp->st_mode & S_IFMT) { case S_IFLNK: if (copy_link(curr, !fts_dne(curr))) { rval = 1; continue; } break; case S_IFDIR: if (!Rflag && !rflag) { warnx("%s is a directory (not copied).", curr->fts_path); (void)fts_set(ftsp, curr, FTS_SKIP); rval = 1; continue; } /* * If the directory doesn't exist, create the new * one with the from file mode plus owner RWX bits, * modified by the umask. Trade-off between being * able to write the directory (if from directory is * 555) and not causing a permissions race. If the * umask blocks owner writes, we fail.. */ if (fts_dne(curr)) { if (mkdir(to.p_path, curr->fts_statp->st_mode | S_IRWXU) < 0) err(1, "%s", to.p_path); } else if (!S_ISDIR(to_stat.st_mode)) errc(1, ENOTDIR, "%s", to.p_path); break; case S_IFBLK: case S_IFCHR: if (Rflag ? copy_special(curr->fts_statp, !fts_dne(curr)) : copy_file(curr, fts_dne(curr))) { rval = 1; continue; } break; case S_IFIFO: if (Rflag ? copy_fifo(curr->fts_statp, !fts_dne(curr)) : copy_file(curr, fts_dne(curr))) { rval = 1; continue; } break; case S_IFSOCK: warnc(EOPNOTSUPP, "%s", curr->fts_path); break; default: if (copy_file(curr, fts_dne(curr))) { rval = 1; continue; } break; } if (vflag) (void)printf("%s -> %s\n", curr->fts_path, to.p_path); } if (errno) err(1, "fts_read"); (void)fts_close(ftsp); return (rval); }
int main(int argc, char *argv[]) { FTS *ftsp; FTSENT *p; u_long clear, newflags, set; long val; int Hflag, Lflag, Rflag, fflag, hflag, vflag; int ch, fts_options, oct, rval; char *flags, *ep; int (*change_flags)(const char *, unsigned long); Hflag = Lflag = Rflag = fflag = hflag = vflag = 0; while ((ch = getopt(argc, argv, "HLPRfhv")) != -1) switch (ch) { case 'H': Hflag = 1; Lflag = 0; break; case 'L': Lflag = 1; Hflag = 0; break; case 'P': Hflag = Lflag = 0; break; case 'R': Rflag = 1; break; case 'f': fflag = 1; break; case 'h': hflag = 1; break; case 'v': vflag++; break; case '?': default: usage(); } argv += optind; argc -= optind; if (argc < 2) usage(); if (Rflag) { fts_options = FTS_PHYSICAL; if (hflag) errx(1, "the -R and -h options " "may not be specified together"); if (Hflag) fts_options |= FTS_COMFOLLOW; if (Lflag) { fts_options &= ~FTS_PHYSICAL; fts_options |= FTS_LOGICAL; } } else fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL; change_flags = hflag ? lchflags : chflags; flags = *argv; if (*flags >= '0' && *flags <= '7') { errno = 0; val = strtol(flags, &ep, 8); if (val < 0) errno = ERANGE; if (errno) err(1, "invalid flags: %s", flags); if (*ep) errx(1, "invalid flags: %s", flags); set = val; oct = 1; } else { if (strtofflags(&flags, &set, &clear)) errx(1, "invalid flag: %s", flags); clear = ~clear; oct = 0; } if ((ftsp = fts_open(++argv, fts_options , 0)) == NULL) err(1, NULL); for (rval = 0; (p = fts_read(ftsp)) != NULL;) { switch (p->fts_info) { case FTS_D: /* Change it at FTS_DP if we're recursive. */ if (!Rflag) fts_set(ftsp, p, FTS_SKIP); continue; case FTS_DNR: /* Warn, chflag, continue. */ warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; break; case FTS_ERR: /* Warn, continue. */ case FTS_NS: warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; continue; case FTS_SL: /* Ignore. */ case FTS_SLNONE: /* * The only symlinks that end up here are ones that * don't point to anything and ones that we found * doing a physical walk. */ if (!hflag) continue; /* FALLTHROUGH */ default: break; } if (oct) newflags = set; else newflags = (p->fts_statp->st_flags | set) & clear; if (newflags == p->fts_statp->st_flags) continue; if ((*change_flags)(p->fts_accpath, newflags) && !fflag) { warn("%s", p->fts_path); rval = 1; } else if (vflag) { (void)printf("%s", p->fts_path); if (vflag > 1) (void)printf(": 0%lo -> 0%lo", (u_long)p->fts_statp->st_flags, newflags); (void)printf("\n"); } } if (errno) err(1, "fts_read"); exit(rval); }
int nftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int nfds __attribute__ ((unused)), int ftwflags) { char * const paths[2] = { (char *)path, NULL }; struct FTW ftw; FTSENT *cur; FTS *ftsp; int error = 0, ftsflags, fnflag, postorder, sverrno; #if 0 /* GLibc allows nfds < 1 and treats it as nfds == 1. Since nfds is not used in this OpenBSD version anyway, just ignore it for Linux compatibility. */ /* XXX - nfds is currently unused */ if (nfds < 1 || nfds > OPEN_MAX) { errno = EINVAL; return (-1); } #endif ftsflags = FTS_COMFOLLOW; if (!(ftwflags & FTW_CHDIR)) ftsflags |= FTS_NOCHDIR; if (ftwflags & FTW_MOUNT) ftsflags |= FTS_XDEV; if (ftwflags & FTW_PHYS) ftsflags |= FTS_PHYSICAL; else ftsflags |= FTS_LOGICAL; postorder = (ftwflags & FTW_DEPTH) != 0; ftsp = fts_open(paths, ftsflags, NULL); if (ftsp == NULL) return (-1); while ((cur = fts_read(ftsp)) != NULL) { switch (cur->fts_info) { case FTS_D: if (postorder) continue; fnflag = FTW_D; break; case FTS_DNR: fnflag = FTW_DNR; break; case FTS_DP: if (!postorder) continue; fnflag = FTW_DP; break; case FTS_F: case FTS_DEFAULT: fnflag = FTW_F; break; case FTS_NS: case FTS_NSOK: fnflag = FTW_NS; break; case FTS_SL: fnflag = FTW_SL; break; case FTS_SLNONE: fnflag = FTW_SLN; break; case FTS_DC: errno = ELOOP; /* FALLTHROUGH */ default: error = -1; goto done; } ftw.base = cur->fts_pathlen - cur->fts_namelen; ftw.level = cur->fts_level; error = fn(cur->fts_path, cur->fts_statp, fnflag, &ftw); if (error != 0) break; } done: sverrno = errno; if (fts_close(ftsp) != 0 && error == 0) error = -1; else errno = sverrno; return (error); }
int WalkTree(char *troot, char *prefix, struct rwcdb *dbh) { /* Traverse tree at troot and insert a record into database dbhandle for each (plain) file. The key of the record is the SHA value of the file; the data part of the record is the pathname of the file (absolute or relative to troot, depending on RelativePathFlag. Returns 0 on success, -1 on error of any kind. On exit, NumEntries has number of records inserted. */ int rc; FTSENT *nextf; /* next element in tree */ FTS *fth; /* open handle for fts() routines */ char *path_argv[2]; unsigned char shabuf[SHA_DIGEST_LENGTH]; int troot_strlen = 0, prefix_strlen = 0; /* save length of troot in this */ int myfd, dlen; char *path; if (RelativePathFlag) { /* find the length of the prefix */ prefix_strlen = strlen(prefix); if (prefix[prefix_strlen] == '/') prefix_strlen--; /* find length of the treeroot */ troot_strlen = strlen(troot); if (troot[troot_strlen] == '/') troot_strlen--; } path_argv[0] = troot; path_argv[1] = NULL; fth = fts_open(path_argv, FTS_PHYSICAL, NULL); if (!fth) { printf("%s: %s\n", troot, strerror(errno)); return(-1); } while ((nextf = fts_read(fth)) != NULL) { if (nextf->fts_info != FTS_F) continue; /* skip all but plain files */ /* compute the SHA of this file */ myfd = open(nextf->fts_name, O_RDONLY, 0); if (myfd < 0) { printf("Skipping %s: %s\n", nextf->fts_path, strerror(errno)); continue; } ComputeViceSHA(myfd, shabuf); close(myfd); /* Construct record to be inserted */ if (RelativePathFlag) { dlen = prefix_strlen + nextf->fts_pathlen - troot_strlen + 1; strcpy(prefix + prefix_strlen, nextf->fts_path + troot_strlen); path = prefix; } else { dlen = nextf->fts_pathlen + 1; path = nextf->fts_path; } /* Insert record into db */ rc = rwcdb_insert(dbh, (char *)shabuf, SHA_DIGEST_LENGTH, path, dlen); if (rc != 1) { printf("%s: insert into database failed\n", nextf->fts_path); rc = -1; goto WalkDone; } /* Successfully added one more entry! */ NumEntries++; if (VerboseFlag) { char temp[2*SHA_DIGEST_LENGTH+1]; ViceSHAtoHex(shabuf, temp, sizeof(temp)); printf("Entry %05d: %s %s\n", NumEntries, temp, path); } else { if ((NumEntries > 99) && (!(NumEntries % 100))) { printf("#"); /* indicate progress */ fflush(stdout); } } } /* loop exited on fts_read() error or when done */ if (errno) { printf("fts_read(): %s\n", strerror(errno)); rc = -1; goto WalkDone; } else { rc = 0; if (!VerboseFlag && (NumEntries > 99)) printf("\n"); /* terminate line of hashes */ } WalkDone: /* rc has return code on entry */ fts_close(fth); return(rc); }
int register_files(char* build, char* project, char* path) { ssize_t res = 0; int loaded = 0; create_tables(); if (SQL("BEGIN")) { return -1; } prune_old_entries(build, project); // // Enumerate the files in the path (DSTROOT) and associate them // with the project name and version in the sqlite database. // Uses ent->fts_number to mark which files we have and have // not seen before. // // Skip the first result, since that is . of the DSTROOT itself. char* path_argv[] = { path, NULL }; FTS* fts = fts_open(path_argv, FTS_PHYSICAL | FTS_COMFOLLOW | FTS_XDEV, compare); FTSENT* ent = fts_read(fts); // throw away the entry for the DSTROOT itself while ((ent = fts_read(fts)) != NULL) { char filename[MAXPATHLEN+1]; char symlink[MAXPATHLEN+1]; ssize_t len; // Filename filename[0] = 0; ent_filename(ent, filename, MAXPATHLEN); // Symlinks symlink[0] = 0; if (ent->fts_info == FTS_SL || ent->fts_info == FTS_SLNONE) { len = readlink(ent->fts_accpath, symlink, MAXPATHLEN); if (len >= 0) symlink[len] = 0; } // Default to empty SHA-1 checksum char* checksum = strdup(" "); // Checksum regular files if (ent->fts_info == FTS_F) { int fd = open(ent->fts_accpath, O_RDONLY); if (fd == -1) { perror(filename); return -1; } int isMachO; res = register_libraries(fd, build, project, filename, &isMachO); lseek(fd, (off_t)0, SEEK_SET); if (isMachO && have_undo_prebinding() == 0) { checksum = calculate_unprebound_digest(ent->fts_accpath); } else { checksum = calculate_digest(fd); } close(fd); } // register regular files and symlinks in the DB if (ent->fts_info == FTS_F || ent->fts_info == FTS_SL || ent->fts_info == FTS_SLNONE) { res = SQL("INSERT INTO files (build, project, path) VALUES (%Q,%Q,%Q)", build, project, filename); ++loaded; } // add all regular files, directories, and symlinks to the manifest if (ent->fts_info == FTS_F || ent->fts_info == FTS_D || ent->fts_info == FTS_SL || ent->fts_info == FTS_SLNONE) { fprintf(stdout, "%s %o %d %d %lld .%s%s%s\n", checksum, ent->fts_statp->st_mode, ent->fts_statp->st_uid, ent->fts_statp->st_gid, (ent->fts_info != FTS_D) ? ent->fts_statp->st_size : (off_t)0, filename, symlink[0] ? " -> " : "", symlink[0] ? symlink : ""); } free(checksum); } fts_close(fts); if (SQL("COMMIT")) { return -1; } fprintf(stderr, "%s - %d files registered.\n", project, loaded); return (int)res; }