int null_or_empty_path(const char *fn) { struct stat st; assert(fn); if (stat(fn, &st) < 0) return -errno; return null_or_empty(&st); }
int null_or_empty_fd(int fd) { struct stat st; assert(fd >= 0); if (fstat(fd, &st) < 0) return -errno; return null_or_empty(&st); }
static int files_add( Hashmap *h, Set *masked, const char *suffix, const char *root, unsigned flags, const char *path) { _cleanup_closedir_ DIR *dir = NULL; const char *dirpath; struct dirent *de; int r; assert(h); assert((flags & CONF_FILES_FILTER_MASKED) == 0 || masked); assert(path); dirpath = prefix_roota(root, path); dir = opendir(dirpath); if (!dir) { if (errno == ENOENT) return 0; return log_debug_errno(errno, "Failed to open directory '%s': %m", dirpath); } FOREACH_DIRENT(de, dir, return -errno) { struct stat st; char *p, *key; /* Does this match the suffix? */ if (suffix && !endswith(de->d_name, suffix)) continue; /* Has this file already been found in an earlier directory? */ if (hashmap_contains(h, de->d_name)) { log_debug("Skipping overridden file '%s/%s'.", dirpath, de->d_name); continue; } /* Has this been masked in an earlier directory? */ if ((flags & CONF_FILES_FILTER_MASKED) && set_contains(masked, de->d_name)) { log_debug("File '%s/%s' is masked by previous entry.", dirpath, de->d_name); continue; } /* Read file metadata if we shall validate the check for file masks, for node types or whether the node is marked executable. */ if (flags & (CONF_FILES_FILTER_MASKED|CONF_FILES_REGULAR|CONF_FILES_DIRECTORY|CONF_FILES_EXECUTABLE)) if (fstatat(dirfd(dir), de->d_name, &st, 0) < 0) { log_debug_errno(errno, "Failed to stat '%s/%s', ignoring: %m", dirpath, de->d_name); continue; } /* Is this a masking entry? */ if ((flags & CONF_FILES_FILTER_MASKED)) if (null_or_empty(&st)) { /* Mark this one as masked */ r = set_put_strdup(masked, de->d_name); if (r < 0) return r; log_debug("File '%s/%s' is a mask.", dirpath, de->d_name); continue; } /* Does this node have the right type? */ if (flags & (CONF_FILES_REGULAR|CONF_FILES_DIRECTORY)) if (!((flags & CONF_FILES_DIRECTORY) && S_ISDIR(st.st_mode)) && !((flags & CONF_FILES_REGULAR) && S_ISREG(st.st_mode))) { log_debug("Ignoring '%s/%s', as it is not a of the right type.", dirpath, de->d_name); continue; } /* Does this node have the executable bit set? */ if (flags & CONF_FILES_EXECUTABLE) /* As requested: check if the file is marked exectuable. Note that we don't check access(X_OK) * here, as we care about whether the file is marked executable at all, and not whether it is * executable for us, because if so, such errors are stuff we should log about. */ if ((st.st_mode & 0111) == 0) { /* not executable */ log_debug("Ignoring '%s/%s', as it is not marked executable.", dirpath, de->d_name); continue; } if (flags & CONF_FILES_BASENAME) { p = strdup(de->d_name); if (!p) return -ENOMEM; key = p; } else { p = strjoin(dirpath, "/", de->d_name); if (!p) return -ENOMEM; key = basename(p); } r = hashmap_put(h, key, p); if (r < 0) { free(p); return log_debug_errno(r, "Failed to add item to hashmap: %m"); } assert(r > 0); } return 0; }