commit 20ccae39452cdf85f57023c8a7f029b0545adfa7 from: Stefan Sperling date: Tue Jul 21 14:19:32 2020 UTC cope with directory entries returned from readdir(3) with type DT_UNKNOWN Such directory entries need special handling to make our directory traversal code work on filesystems that do not support the d_type optimization. I found this problem because references stored in the refs/ directory were not shown by 'got log' and 'tog log' when a repository is mounted over NFS. helpful feedback + ok millert@ commit - 4da1bbe9cccd432c3afb675e07eedc58bc12ae31 commit + 20ccae39452cdf85f57023c8a7f029b0545adfa7 blob - 57b823a4f04403e0dfd1d1e79cc8fe80f6dbc79c blob + 0c75579cb470e296c9a97a5ea0ac1e14f342971a --- include/got_path.h +++ include/got_path.h @@ -21,6 +21,8 @@ #define GOT_DEFAULT_DIR_MODE (S_IFDIR | \ S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH) +struct dirent; + /* Determine whether a path is an absolute path. */ int got_path_is_absolute(const char *); @@ -102,6 +104,20 @@ int got_path_dir_is_empty(const char *); /* dirname(3) with error handling and dynamically allocated result. */ const struct got_error *got_path_dirname(char **, const char *); +/* + * Obtain the file type of a given directory entry. + * + * If the entry has some type other than DT_UNKNOWN, resolve to this type. + * + * Otherwise, attempt to resolve the type of a DT_UNKNOWN directory + * entry with lstat(2), though the result may still be DT_UNKNOWN. + * This is a fallback to accommodate filesystems which do not provide + * directory entry type information. + * DT_UNKNOWN directory entries occur on NFS mounts without "readdir plus" RPC. + */ +const struct got_error *got_path_dirent_type(int *, const char *, + struct dirent *); + /* basename(3) with dynamically allocated result. */ const struct got_error *got_path_basename(char **, const char *); blob - 656685591bdfde81e5346507229c0bd87d2fa18c blob + c3bd9dd19f3da3ee47a71c5a07cb1bf174eaf2d7 --- lib/fileindex.c +++ lib/fileindex.c @@ -933,10 +933,23 @@ walk_dir(struct got_pathlist_entry **next, struct got_ struct dirent *de = dle->data; DIR *subdir = NULL; int subdirfd = -1; + int type; *next = NULL; - if (de->d_type == DT_DIR) { + if (de->d_type == DT_UNKNOWN) { + /* Occurs on NFS mounts without "readdir plus" RPC. */ + char *dir_path; + if (asprintf(&dir_path, "%s/%s", rootpath, path) == -1) + return got_error_from_errno("asprintf"); + err = got_path_dirent_type(&type, dir_path, de); + free(dir_path); + if (err) + return err; + } else + type = de->d_type; + + if (type == DT_DIR) { char *subpath; char *subdirpath; struct got_pathlist_head subdirlist; blob - be971e697a2b76f0479272ad328aa87e1fe8450d blob + 549cac5f15819dd3e37527467f02c394f51d5006 --- lib/path.c +++ lib/path.c @@ -375,6 +375,53 @@ got_path_dirname(char **parent, const char *path) } const struct got_error * +got_path_dirent_type(int *type, const char *path_parent, struct dirent *dent) +{ + const struct got_error *err = NULL; + char *path_child; + struct stat sb; + + if (dent->d_type != DT_UNKNOWN) { + *type = dent->d_type; + return NULL; + } + + *type = DT_UNKNOWN; + + /* + * This is a fallback to accommodate filesystems which do not + * provide directory entry type information. DT_UNKNOWN directory + * entries occur on NFS mounts without "readdir plus" RPC. + */ + + if (asprintf(&path_child, "%s/%s", path_parent, dent->d_name) == -1) + return got_error_from_errno("asprintf"); + + if (lstat(path_child, &sb) == -1) { + err = got_error_from_errno2("lstat", path_child); + goto done; + } + + if (S_ISFIFO(sb.st_mode)) + *type = DT_FIFO; + else if (S_ISCHR(sb.st_mode)) + *type = DT_CHR; + else if (S_ISDIR(sb.st_mode)) + *type = DT_DIR; + else if (S_ISBLK(sb.st_mode)) + *type = DT_BLK; + else if (S_ISLNK(sb.st_mode)) + *type = DT_LNK; + else if (S_ISREG(sb.st_mode)) + *type = DT_REG; + else if (S_ISSOCK(sb.st_mode)) + *type = DT_SOCK; +done: + free(path_child); + return err; +} + +const struct got_error * got_path_basename(char **s, const char *path) { char *base; blob - a4aa416aa740d7f102a9b038776457ef3096c1e4 blob + 3c2632d04567f59182586129b87e15927dbe4b0d --- lib/reference.c +++ lib/reference.c @@ -826,6 +826,7 @@ gather_on_disk_refs(struct got_reflist_head *refs, con struct dirent *dent; struct got_reference *ref; char *child; + int type; dent = readdir(d); if (dent == NULL) @@ -835,7 +836,11 @@ gather_on_disk_refs(struct got_reflist_head *refs, con strcmp(dent->d_name, "..") == 0) continue; - switch (dent->d_type) { + err = got_path_dirent_type(&type, path_subdir, dent); + if (err) + break; + + switch (type) { case DT_REG: err = open_ref(&ref, path_refs, subdir, dent->d_name, 0); blob - 7531c15dc53560bdff8f322c2b1f6f3c61d849f4 blob + dec92533e2bad4d1d830b4d80f307bdc5314cb8a --- lib/repository.c +++ lib/repository.c @@ -1579,6 +1579,7 @@ write_tree(struct got_object_id **new_tree_id, const c nentries = 0; while ((de = readdir(dir)) != NULL) { int ignore = 0; + int type; if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) @@ -1592,7 +1593,12 @@ write_tree(struct got_object_id **new_tree_id, const c } if (ignore) continue; - if (de->d_type == DT_DIR) { + + err = got_path_dirent_type(&type, path_dir, de); + if (err) + goto done; + + if (type == DT_DIR) { err = import_subdir(&new_te, de, path_dir, ignores, repo, progress_cb, progress_arg); if (err) { @@ -1601,7 +1607,7 @@ write_tree(struct got_object_id **new_tree_id, const c err = NULL; continue; } - } else if (de->d_type == DT_REG) { + } else if (type == DT_REG) { err = import_file(&new_te, de, path_dir, repo); if (err) goto done;