static int process_path (char *pathname, char *name, bool leaf, char *parent, mode_t mode, ino_t inum) { struct stat stat_buf; static dev_t root_dev; /* Device ID of current argument pathname. */ int i; struct predicate *eval_tree; eval_tree = get_eval_tree (); /* Assume it is a non-directory initially. */ stat_buf.st_mode = 0; /* The caller usually knows the inode number, either from readdir or * a *stat call. We use that value (the caller passes 0 to indicate * ignorance of the inode number). */ stat_buf.st_ino = inum; state.rel_pathname = name; state.type = 0; state.have_stat = false; state.have_type = false; state.already_issued_stat_error_msg = false; if (!digest_mode (&mode, pathname, name, &stat_buf, leaf)) return 0; if (!S_ISDIR (state.type)) { if (state.curdepth >= options.mindepth) apply_predicate (pathname, &stat_buf, eval_tree); return 0; } /* From here on, we're working on a directory. */ /* Now we really need to stat the directory, even if we know the * type, because we need information like struct stat.st_rdev. */ if (get_statinfo (pathname, name, &stat_buf) != 0) return 0; state.have_stat = true; mode = state.type = stat_buf.st_mode; /* use full info now that we have it. */ state.stop_at_current_level = options.maxdepth >= 0 && state.curdepth >= options.maxdepth; /* If we've already seen this directory on this branch, don't descend it again. */ for (i = 0; i <= dir_curr; i++) if (stat_buf.st_ino == dir_ids[i].ino && stat_buf.st_dev == dir_ids[i].dev) { state.stop_at_current_level = true; issue_loop_warning (name, pathname, i); } if (dir_alloc <= ++dir_curr) { dir_alloc += DIR_ALLOC_STEP; dir_ids = (struct dir_id *) xrealloc ((char *) dir_ids, dir_alloc * sizeof (struct dir_id)); } dir_ids[dir_curr].ino = stat_buf.st_ino; dir_ids[dir_curr].dev = stat_buf.st_dev; if (options.stay_on_filesystem) { if (state.curdepth == 0) root_dev = stat_buf.st_dev; else if (stat_buf.st_dev != root_dev) state.stop_at_current_level = true; } if (options.do_dir_first && state.curdepth >= options.mindepth) apply_predicate (pathname, &stat_buf, eval_tree); if (options.debug_options & DebugSearch) fprintf (stderr, "pathname = %s, stop_at_current_level = %d\n", pathname, state.stop_at_current_level); if (state.stop_at_current_level == false) { /* Scan directory on disk. */ process_dir (pathname, name, strlen (pathname), &stat_buf, parent); } if (options.do_dir_first == false && state.curdepth >= options.mindepth) { /* The fields in 'state' are now out of date. Correct them. */ if (!digest_mode (&mode, pathname, name, &stat_buf, leaf)) return 0; if (0 == dir_curr) { at_top (pathname, mode, stat_buf.st_ino, &stat_buf, do_process_predicate); } else { do_process_predicate (pathname, name, mode, stat_buf.st_ino, &stat_buf); } } dir_curr--; return 1; }
static void consider_visiting (FTS *p, FTSENT *ent) { struct stat statbuf; mode_t mode; int ignore, isdir; if (options.debug_options & DebugSearch) fprintf (stderr, "consider_visiting (early): %s: " "fts_info=%-6s, fts_level=%2d, prev_depth=%d " "fts_path=%s, fts_accpath=%s\n", quotearg_n_style (0, options.err_quoting_style, ent->fts_path), get_fts_info_name (ent->fts_info), (int)ent->fts_level, prev_depth, quotearg_n_style (1, options.err_quoting_style, ent->fts_path), quotearg_n_style (2, options.err_quoting_style, ent->fts_accpath)); if (ent->fts_info == FTS_DP) { left_dir (); } else if (ent->fts_level > prev_depth || ent->fts_level==0) { left_dir (); } inside_dir (p->fts_cwd_fd); prev_depth = ent->fts_level; statbuf.st_ino = ent->fts_statp->st_ino; /* Cope with various error conditions. */ if (ent->fts_info == FTS_ERR || ent->fts_info == FTS_DNR) { nonfatal_target_file_error (ent->fts_errno, ent->fts_path); return; } else if (ent->fts_info == FTS_DC) { issue_loop_warning (ent); error_severity (EXIT_FAILURE); return; } else if (ent->fts_info == FTS_SLNONE) { /* fts_read() claims that ent->fts_accpath is a broken symbolic * link. That would be fine, but if this is part of a symbolic * link loop, we diagnose the problem and also ensure that the * eventual return value is nonzero. Note that while the path * we stat is local (fts_accpath), we print the full path name * of the file (fts_path) in the error message. */ if (symlink_loop (ent->fts_accpath)) { nonfatal_target_file_error (ELOOP, ent->fts_path); return; } } else if (ent->fts_info == FTS_NS) { if (ent->fts_level == 0) { /* e.g., nonexistent starting point */ nonfatal_target_file_error (ent->fts_errno, ent->fts_path); return; } else { /* The following if statement fixes Savannah bug #19605 * (failure to diagnose a symbolic link loop) */ if (symlink_loop (ent->fts_accpath)) { nonfatal_target_file_error (ELOOP, ent->fts_path); return; } else { nonfatal_target_file_error (ent->fts_errno, ent->fts_path); /* Continue despite the error, as file name without stat info * might be better than not even processing the file name. This * can lead to repeated error messages later on, though, if a * predicate requires stat information. * * Not printing an error message here would be even more wrong, * though, as this could cause the contents of a directory to be * silently ignored, as the directory wouldn't be identified as * such. */ } } } /* Cope with the usual cases. */ if (ent->fts_info == FTS_NSOK || ent->fts_info == FTS_NS /* e.g. symlink loop */) { assert (!state.have_stat); assert (ent->fts_info == FTS_NSOK || state.type == 0); mode = state.type; } else { state.have_stat = true; state.have_type = true; statbuf = *(ent->fts_statp); state.type = mode = statbuf.st_mode; if (00000 == mode) { /* Savannah bug #16378. */ error (0, 0, _("WARNING: file %s appears to have mode 0000"), quotearg_n_style (0, options.err_quoting_style, ent->fts_path)); } } /* update state.curdepth before calling digest_mode(), because digest_mode * may call following_links(). */ state.curdepth = ent->fts_level; if (mode) { if (!digest_mode (&mode, ent->fts_path, ent->fts_name, &statbuf, 0)) return; } /* examine this item. */ ignore = 0; isdir = S_ISDIR(mode) || (FTS_D == ent->fts_info) || (FTS_DP == ent->fts_info) || (FTS_DC == ent->fts_info); if (isdir && (ent->fts_info == FTS_NSOK)) { /* This is a directory, but fts did not stat it, so * presumably would not be planning to search its * children. Force a stat of the file so that the * children can be checked. */ fts_set (p, ent, FTS_AGAIN); return; } if (options.maxdepth >= 0) { if (ent->fts_level >= options.maxdepth) { fts_set (p, ent, FTS_SKIP); /* descend no further */ if (ent->fts_level > options.maxdepth) ignore = 1; /* don't even look at this one */ } } if ( (ent->fts_info == FTS_D) && !options.do_dir_first ) { /* this is the preorder visit, but user said -depth */ ignore = 1; } else if ( (ent->fts_info == FTS_DP) && options.do_dir_first ) { /* this is the postorder visit, but user didn't say -depth */ ignore = 1; } else if (ent->fts_level < options.mindepth) { ignore = 1; } if (options.debug_options & DebugSearch) fprintf (stderr, "consider_visiting (late): %s: " "fts_info=%-6s, isdir=%d ignore=%d have_stat=%d have_type=%d \n", quotearg_n_style (0, options.err_quoting_style, ent->fts_path), get_fts_info_name (ent->fts_info), isdir, ignore, state.have_stat, state.have_type); if (!ignore) { visit (p, ent, &statbuf); } if (ent->fts_info == FTS_DP) { /* we're leaving a directory. */ state.stop_at_current_level = false; } }
static void consider_visiting(FTS *p, FTSENT *ent) { struct stat statbuf; mode_t mode; int ignore, isdir; if (options.debug_options & DebugSearch) fprintf(stderr, "consider_visiting: fts_info=%-6s, fts_level=%2d, prev_depth=%d " "fts_path=%s, fts_accpath=%s\n", get_fts_info_name(ent->fts_info), (int)ent->fts_level, prev_depth, quotearg_n_style(0, options.err_quoting_style, ent->fts_path), quotearg_n_style(1, options.err_quoting_style, ent->fts_accpath)); if (ent->fts_info == FTS_DP) { left_dir(); } else if (ent->fts_level > prev_depth || ent->fts_level==0) { left_dir(); } inside_dir(p->fts_cwd_fd); prev_depth = ent->fts_level; /* Cope with various error conditions. */ if (ent->fts_info == FTS_ERR || ent->fts_info == FTS_DNR) { error(0, ent->fts_errno, "%s", safely_quote_err_filename(0, ent->fts_path)); error_severity(1); return; } else if (ent->fts_info == FTS_DC) { issue_loop_warning(ent); error_severity(1); return; } else if (ent->fts_info == FTS_SLNONE) { /* fts_read() claims that ent->fts_accpath is a broken symbolic * link. That would be fine, but if this is part of a symbolic * link loop, we diagnose the problem and also ensure that the * eventual return value is nonzero. Note that while the path * we stat is local (fts_accpath), we print the full path name * of the file (fts_path) in the error message. */ if (symlink_loop(ent->fts_accpath)) { error(0, ELOOP, "%s", safely_quote_err_filename(0, ent->fts_path)); error_severity(1); return; } } else if (ent->fts_info == FTS_NS) { if (ent->fts_level == 0) { /* e.g., nonexistent starting point */ error(0, ent->fts_errno, "%s", safely_quote_err_filename(0, ent->fts_path)); error_severity(1); /* remember problem */ return; } else { /* The following if statement fixes Savannah bug #19605 * (failure to diagnose a symbolic link loop) */ if (symlink_loop(ent->fts_accpath)) { error(0, ELOOP, "%s", safely_quote_err_filename(0, ent->fts_path)); error_severity(1); return; } } } /* Cope with the usual cases. */ if (ent->fts_info == FTS_NSOK || ent->fts_info == FTS_NS /* e.g. symlink loop */) { assert (!state.have_stat); assert (!state.have_type); state.type = mode = 0; } else { state.have_stat = true; state.have_type = true; statbuf = *(ent->fts_statp); state.type = mode = statbuf.st_mode; if (00000 == mode) { /* Savannah bug #16378. */ error(0, 0, _("Warning: file %s appears to have mode 0000"), quotearg_n_style(0, options.err_quoting_style, ent->fts_path)); } } if (mode) { if (!digest_mode(mode, ent->fts_path, ent->fts_name, &statbuf, 0)) return; } /* examine this item. */ ignore = 0; isdir = S_ISDIR(mode) || (FTS_D == ent->fts_info) || (FTS_DP == ent->fts_info) || (FTS_DC == ent->fts_info); if (isdir && (ent->fts_info == FTS_NSOK)) { /* This is a directory, but fts did not stat it, so * presumably would not be planning to search its * children. Force a stat of the file so that the * children can be checked. */ fts_set(p, ent, FTS_AGAIN); return; } if (options.maxdepth >= 0) { if (ent->fts_level >= options.maxdepth) { fts_set(p, ent, FTS_SKIP); /* descend no further */ if (ent->fts_level > options.maxdepth) ignore = 1; /* don't even look at this one */ } } if ( (ent->fts_info == FTS_D) && !options.do_dir_first ) { /* this is the preorder visit, but user said -depth */ ignore = 1; } else if ( (ent->fts_info == FTS_DP) && options.do_dir_first ) { /* this is the postorder visit, but user didn't say -depth */ ignore = 1; } else if (ent->fts_level < options.mindepth) { ignore = 1; } if (!ignore) { visit(p, ent, &statbuf); } /* XXX: if we allow a build-up of pending arguments for "-execdir foo {} +" * we need to execute them in the same directory as we found the item. * If we are trying to do "find a -execdir echo {} +", we will need to * echo * a while in the original working directory * b while in a * c while in b (just before leaving b) * * These restrictions are hard to satisfy while using fts(). The reason is * that it doesn't tell us just before we leave a directory. For the moment, * we punt and don't allow the arguments to build up. */ if (state.execdirs_outstanding) { show_outstanding_execdirs(stderr); run_in_dir(p->fts_cwd_fd, complete_execdirs_cb, NULL); } if (ent->fts_info == FTS_DP) { /* we're leaving a directory. */ state.stop_at_current_level = false; } }