-mode_t get_mode(int fd) {
- if (fd <= 0) {
- errno = ENOENT;
- return ACL_ERROR;
- }
-
- struct stat s;
- int result = fstat(fd, &s);
-
- if (result == 0) {
- return s.st_mode;
- }
- else {
- /* errno will be set already by lstat() */
- return result;
- }
+int safe_open_ex(int at_fd, char* pathname, int flags) {
+ if (pathname != NULL && strlen(pathname) == 0) {
+ /* Oops, went one level to deep with nothing to do. */
+ return at_fd;
+ }
+
+ char* firstslash = strchr(pathname, '/');
+ if (firstslash == NULL) {
+ /* No more slashes, this is the base case. */
+ int r = openat(at_fd, pathname, flags);
+ return r;
+ }
+
+ /* Temporarily disable the slash, so that the subsequent call to
+ openat() opens only the next directory (and doesn't recurse). */
+ *firstslash = '\0';
+ int fd = safe_open_ex(at_fd, pathname, flags);
+ if (fd == OPEN_ERROR) {
+ if (errno != ELOOP) {
+ /* Don't output anything if we ignore a symlink */
+ perror("safe_open_ex (safe_open_ex)");
+ }
+ return OPEN_ERROR;
+ }
+
+ /* The ++ is safe because there needs to be at least a null byte
+ after the first slash, even if it's the last real character in
+ the string. */
+ int result = safe_open_ex(fd, firstslash+1, flags);
+ if (close(fd) == CLOSE_ERROR) {
+ perror("safe_open_ex (close)");
+ return OPEN_ERROR;
+ }
+ return result;