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@
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
diff --git a/include/got_path.h b/include/got_path.h
index 57b823a..0c75579 100644
--- a/include/got_path.h
+++ b/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 *);
diff --git a/lib/fileindex.c b/lib/fileindex.c
index 6566855..c3bd9dd 100644
--- a/lib/fileindex.c
+++ b/lib/fileindex.c
@@ -933,10 +933,23 @@ walk_dir(struct got_pathlist_entry **next, struct got_fileindex *fileindex,
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;
diff --git a/lib/path.c b/lib/path.c
index be971e6..549cac5 100644
--- a/lib/path.c
+++ b/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;
diff --git a/lib/reference.c b/lib/reference.c
index a4aa416..3c2632d 100644
--- a/lib/reference.c
+++ b/lib/reference.c
@@ -826,6 +826,7 @@ gather_on_disk_refs(struct got_reflist_head *refs, const char *path_refs,
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, const char *path_refs,
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);
diff --git a/lib/repository.c b/lib/repository.c
index 7531c15..dec9253 100644
--- a/lib/repository.c
+++ b/lib/repository.c
@@ -1579,6 +1579,7 @@ write_tree(struct got_object_id **new_tree_id, const char *path_dir,
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 char *path_dir,
}
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 char *path_dir,
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;