Bug 226166 - [PATCH] fts(3): setting FTS_FOLLOW for children does not work as expected when FTS_NOCHDIR is set
Summary: [PATCH] fts(3): setting FTS_FOLLOW for children does not work as expected whe...
Status: New
Alias: None
Product: Base System
Classification: Unclassified
Component: bin (show other bugs)
Version: CURRENT
Hardware: Any Any
: --- Affects Some People
Assignee: freebsd-bugs mailing list
URL:
Keywords: patch
Depends on:
Blocks:
 
Reported: 2018-02-24 13:11 UTC by Jan Kokemüller
Modified: 2018-02-25 18:02 UTC (History)
1 user (show)

See Also:


Attachments
patch to fix FTS_FOLLOW when set on entries returned by fts_children() (1.26 KB, patch)
2018-02-24 13:11 UTC, Jan Kokemüller
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Jan Kokemüller 2018-02-24 13:11:01 UTC
Created attachment 190957 [details]
patch to fix FTS_FOLLOW when set on entries returned by fts_children()

Reading the fts(3) man page something like the example below should work. This sets FTS_FOLLOW on a child named 'symlink' which is a symlink to another folder. fts_read will now recurse into this folder, even though FTS_PHYSICAL is set. Right now it won't recurse, however, when FTS_NOCHDIR is also set.

The problem is a stale fts_accpath. This leads to fts_stat() calling stat on the wrong file. The attached patch attempts to fix this by switching the order of the fts_stat() and the fts_accpath update.



#include <err.h>
#include <fts.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
main()
{
	FTS *fts;
	FTSENT *ftsent, *children;
	char *fts_args[] = { ".", NULL };

	fts = fts_open(fts_args, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
	if (!fts) {
		err(1, "fts");
	}

	while ((ftsent = fts_read(fts))) {
		fprintf(stderr, "got file: %s\n", ftsent->fts_path);

		children = fts_children(fts, 0);

		for (FTSENT *child = children; child;
		     child = child->fts_link) {
			if (!strcmp("symlink", child->fts_name)) {
				if (fts_set(fts, child, FTS_FOLLOW)) {
					err(1, "fts_set");
				}
			}
		}
	}

	if (fts_close(fts) < 0) {
		warn("fts_close");
	}
}