Directory traversal with chdir() instead of with absolute paths - c

In Chapter 4 of the book "Advanced Programming in the Unix Environment," which covers files and directories, there is a code sample which aims to be like the ftw command and traverse a file hierarchy. It uses a pointer to an absolute file path, as well as a recursive function with a callback to traverse the directory, using calls to opendir() and readdir() in the process.
There is an exercise in which readers are asked to use chdir() and file names instead of using the absolute paths to accomplish the same task and to compare the times of the two programs. I wrote a program using chdir() and did not notice a difference in the time. Is this expected? I would have thought that the additional call to chdir() would add some overhead. Is it maybe a relatively trivial call? Any insight would be appreciated.
Here's the recursive function using absolute paths:
static int /* we return whatever func() returns */
dopath(Myfunc* func)
{
struct stat statbuf;
struct dirent *dirp;
DIR *dp;
int ret;
char *ptr;
if (lstat(fullpath, &statbuf) < 0) /* stat error */
return(func(fullpath, &statbuf, FTW_NS));
if (S_ISDIR(statbuf.st_mode) == 0) /* not a directory */
return(func(fullpath, &statbuf, FTW_F));
/*
* It's a directory. First call func() for the directory,
* then process each filename in the directory.
*/
if ((ret = func(fullpath, &statbuf, FTW_D)) != 0)
return(ret);
ptr = fullpath + strlen(fullpath); /* point to end of fullpath */
*ptr++ = '/';
*ptr = 0;
if ((dp = opendir(fullpath)) == NULL) /* can't read directory */
return(func(fullpath, &statbuf, FTW_DNR));
while ((dirp = readdir(dp)) != NULL) {
if (strcmp(dirp->d_name, ".") == 0 ||
strcmp(dirp->d_name, "..") == 0)
continue; /* ignore dot and dot-dot */
strcpy(ptr, dirp->d_name); /* append name after slash */
if ((ret = dopath(func)) != 0) /* recursive */
break; /* time to leave */
}
ptr[-1] = 0; /* erase everything from slash onwards */
if (closedir(dp) < 0)
err_ret("can't close directory %s", fullpath);
return(ret);
}
And here's the function with my changes:
static int /* we return whatever func() returns */
dopath(Myfunc* func, char* path)
{
struct stat statbuf;
struct dirent *dirp;
DIR *dp;
int ret;
if (lstat(path, &statbuf) < 0) /* stat error */
return(func(path, &statbuf, FTW_NS));
if (S_ISDIR(statbuf.st_mode) == 0) /* not a directory */
return(func(path, &statbuf, FTW_F));
/*
* It's a directory. First call func() for the directory,
* then process each filename in the directory.
*/
if ((ret = func(path, &statbuf, FTW_D)) != 0)
return(ret);
if ( chdir(path) < 0 )
return(func(path, &statbuf, FTW_DNR));
if ((dp = opendir(".")) == NULL) /* can't read directory */
return(func(path, &statbuf, FTW_DNR));
while ((dirp = readdir(dp)) != NULL) {
if (strcmp(dirp->d_name, ".") == 0 ||
strcmp(dirp->d_name, "..") == 0)
continue; /* ignore dot and dot-dot */
if ((ret = dopath(func, dirp->d_name)) != 0) /* recursive */
break; /* time to leave */
}
if ( chdir("..") < 0 )
err_ret("can't go up directory");
if (closedir(dp) < 0)
err_ret("can't close directory %s", fullpath);
return(ret);
}

I don't think you should expect a substantial time performance difference between the absolute path version and the chdir() version. Rather, the pros and cons of both versions are as follows:
The full pathname version might not be able to traverse very deep directory structures because the length of the full pathname eventually exceeds PATH_MAX. The chdir() version does not have this problem.
The chdir() version manipulates the pwd, which is generally considered bad practice if you can avoid it: it's not thread-safe, and the end user might expect it to be left alone. For example filenames given on the command line and used by a different part of the program might be relative to what the user thought the pwd was, which breaks when you change it.
The chdir() version might go out of control when backing up to a higher directory (chdir("..")) if special care is not taken and the directory structure changes while it is being traversed. Then again the full pathname version might break in a different way under these circumstances...
The openat() family of functions available on modern POSIX systems offer the best of both worlds. If these functions are available, openat() together with fdopendir(), fstatat(), etc... make for a really nice implementation of directory walking.

Related

Why is the lstat() function failing?

I'm having problem understanding how lstat() in c actually works. I have already read the following documentation but still have some problems understating how lstat() really works and why doesn't it work the way i
I use it.
https://man7.org/linux/man-pages/man0/sys_stat.h.0p.html
https://www.ibm.com/docs/en/zos/2.1.0?topic=functions-lstat-get-status-file-symbolic-link
https://man7.org/linux/man-pages/man2/lstat.2.html
https://man7.org/linux/man-pages/man3/lstat.3p.html
https://man7.org/linux/man-pages/man3/fstatat.3p.html
Basically what I want to do is write a program similar to the command find, that list all files/directories and subdirectories. I want to use lstat() to check if a file is a symbolic link and if so skip it. The st_mode gives information about the file type.
#includes ...
void error_handling(char* msg){
perror(msg);
exit(1);
}
void print_cwd(){} //prints the current working directory
void print_path(char* path){} //prints the current path
void handle_path(char * path) {
int success = chdir(path);
if (success != 0) {
if (errno == ENOTDIR || errno == ENOENT) {
return; // OK, just ignore non-directory entries for recursion
} else {
error_handling("chdir");
}
}
DIR *current_dir = opendir(".");
if (current_dir == NULL) {
error_handling("opendir");
}
struct dirent *current_dirent;
while (1) {
errno = 0;
current_dirent = readdir(current_dir);
if (current_dirent == NULL) {
if (errno != 0) { // readdir doesn't change errno on success, hence, it should be zero
error_handling("readdir");
}
break;
}
char *name = current_dirent->d_name;
if (strcmp(name, "..") == 0 || strcmp(name, ".") == 0) {
continue;
}
print_path(current_dirent->d_name);
handle_path(current_dirent->d_name);
}
closedir(current_dir);
/* it doesn't matter where i call lstat() it always falis, be it before opening the directory or anywhere else. As soon as this is calledit fails
struct stat buf;
int s = lstat(path, &buf);
if(s != 0)
error_handling("lstat");
*/
chdir("..");
}
It fails with the following error: lstat: No such file or directory
As described here https://man7.org/linux/man-pages/man2/lstat.2.html in the return value section, these function return 0 on success and -1 on error.
According to this post I found on here Symlinks and File Redirection you should call lstat() after you call open(). Still fails with the same error

Towards understanding availability of xdg-open

I want to open an image, and in Windows I do:
#include <windows.h>
..
ShellExecute(NULL, "open", "https://gsamaras.files.wordpress.com/2018/11/chronosgod.png", NULL, NULL, SW_SHOWNORMAL);
I would like to use a Linux approach, where it's so much easier to run something on the fly. Example:
char s[100];
snprintf(s, sizeof s, "%s %s", "xdg-open", "https://gsamaras.files.wordpress.com/2018/11/chronosgod.png");
system(s);
In my Ubuntu, it works. However, when running that in Wandbox (Live Demo), or in any other online compiler, I would most likely get an error:
sh: 1: xdg-open: not found
despite the fact that these online compilers seem to live in Linux (checked). I don't expect the online compiler to open a browser for me, but I did expect the code to run without an error. Ah, and forget Mac (personal laptop, limiting my machines).
Since I have no other Linux machine to check, my question is: Can I expect that this code will work in most of the major Linux distributions?
Maybe the fact that it failed on online compilers is misleading.
PS: This is for my post on God of Time, so no worries about security.
Although Antti Haapala already completely answered the question, I thought some comments about the approach, and an example function making safe use trivial, might be useful.
xdg-open is part of desktop integration utilities from freedesktop.org, as part of the Portland project. One can expect them to be available on any computer running a desktop environment participating in freedesktop.org. This includes GNOME, KDE, and Xfce.
Simply put, this is the recommended way of opening a resource (be it a file or URL) when a desktop environment is in use, in whatever application the user prefers.
If there is no desktop environment in use, then there is no reason to expect xdg-open to be available either.
For Linux, I would suggest using a dedicated function, perhaps along the following lines. First, a couple of internal helper functions:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
//
// SPDX-License-Identifier: CC0-1.0
//
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <dirent.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/* Number of bits in an unsigned long. */
#define ULONG_BITS (CHAR_BIT * sizeof (unsigned long))
/* Helper function to open /dev/null to a specific descriptor.
*/
static inline int devnullfd(const int fd)
{
int tempfd;
/* Sanity check. */
if (fd == -1)
return errno = EINVAL;
do {
tempfd = open("/dev/null", O_RDWR | O_NOCTTY);
} while (tempfd == -1 && errno == EINTR);
if (tempfd == -1)
return errno;
if (tempfd != fd) {
if (dup2(tempfd, fd) == -1) {
const int saved_errno = errno;
close(tempfd);
return errno = saved_errno;
}
if (close(tempfd) == -1)
return errno;
}
return 0;
}
/* Helper function to close all except small descriptors
specified in the mask. For obvious reasons, this is not
thread safe, and is only intended to be used in recently
forked child processes. */
static void closeall(const unsigned long mask)
{
DIR *dir;
struct dirent *ent;
int dfd;
dir = opendir("/proc/self/fd/");
if (!dir) {
/* Cannot list open descriptors. Just try and close all. */
const long fd_max = sysconf(_SC_OPEN_MAX);
long fd;
for (fd = 0; fd < ULONG_BITS; fd++)
if (!(mask & (1uL << fd)))
close(fd);
for (fd = ULONG_BITS; fd <= fd_max; fd++)
close(fd);
return;
}
dfd = dirfd(dir);
while ((ent = readdir(dir)))
if (ent->d_name[0] >= '0' && ent->d_name[0] <= '9') {
const char *p = &ent->d_name[1];
int fd = ent->d_name[0] - '0';
while (*p >= '0' && *p <= '9')
fd = (10 * fd) + *(p++) - '0';
if (*p)
continue;
if (fd == dfd)
continue;
if (fd < ULONG_MAX && (mask & (1uL << fd)))
continue;
close(fd);
}
closedir(dir);
}
closeall(0) tries hard to close all open file descriptors, and devnullfd(fd) tries to open fd to /dev/null. These are used to make sure that even if the user spoofs xdg-open, no file descriptors are leaked; only the file name or URL is passed.
On non-Linux POSIXy systems, you can replace them with something more suitable. On BSDs, use closefrom(), and handle the first ULONG_MAX descriptors in a loop.
The xdg_open(file-or-url) function itself is something along the lines of
/* Launch the user-preferred application to open a file or URL.
Returns 0 if success, an errno error code otherwise.
*/
int xdg_open(const char *file_or_url)
{
pid_t child, p;
int status;
/* Sanity check. */
if (!file_or_url || !*file_or_url)
return errno = EINVAL;
/* Fork the child process. */
child = fork();
if (child == -1)
return errno;
else
if (!child) {
/* Child process. */
uid_t uid = getuid(); /* Real, not effective, user. */
gid_t gid = getgid(); /* Real, not effective, group. */
/* Close all open file descriptors. */
closeall(0);
/* Redirect standard streams, if possible. */
devnullfd(STDIN_FILENO);
devnullfd(STDOUT_FILENO);
devnullfd(STDERR_FILENO);
/* Drop elevated privileges, if any. */
if (setresgid(gid, gid, gid) == -1 ||
setresuid(uid, uid, uid) == -1)
_Exit(98);
/* Have the child process execute in a new process group. */
setsid();
/* Execute xdg-open. */
execlp("xdg-open", "xdg-open", file_or_url, (char *)0);
/* Failed. xdg-open uses 0-5, we return 99. */
_Exit(99);
}
/* Reap the child. */
do {
status = 0;
p = waitpid(child, &status, 0);
} while (p == -1 && errno == EINTR);
if (p == -1)
return errno;
if (!WIFEXITED(status)) {
/* Killed by a signal. Best we can do is I/O error, I think. */
return errno = EIO;
}
switch (WEXITSTATUS(status)) {
case 0: /* No error. */
return errno = 0; /* It is unusual, but robust to explicitly clear errno. */
case 1: /* Error in command line syntax. */
return errno = EINVAL; /* Invalid argument */
case 2: /* File does not exist. */
return errno = ENOENT; /* No such file or directory */
case 3: /* A required tool could not be found. */
return errno = ENOSYS; /* Not implemented */
case 4: /* Action failed. */
return errno = EPROTO; /* Protocol error */
case 98: /* Identity shenanigans. */
return errno = EACCES; /* Permission denied */
case 99: /* xdg-open does not exist. */
return errno = ENOPKG; /* Package not installed */
default:
/* None of the other values should occur. */
return errno = ENOSYS; /* Not implemented */
}
}
As already mentioned, it tries hard to close all open file descriptors, redirects the standard streams to /dev/null, ensures the effective and real identity matches (in case this is used in a setuid binary), and passes success/failure using the child process exit status.
The setresuid() and setresgid() calls are only available on OSes that have saved user and group ids. On others, use seteuid(uid) and setegid() instead.
This implementation tries to balance user configurability with security. Users can set the PATH so that their favourite xdg-open gets executed, but the function tries to ensure that no sensitive information or privileges are leaked to that process.
(Environment variables could be filtered, but they should not contain sensitive information in the first place, and we don't really know which ones a desktop environment uses. So better not mess with them, to keep user surprises to a minimum.)
As a minimal test main(), try the following:
int main(int argc, char *argv[])
{
int arg, status;
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s FILE-OR-URL ...\n", argv[0]);
fprintf(stderr, "\n");
fprintf(stderr, "This example program opens each specified file or URL\n");
fprintf(stderr, "xdg-open(1), and outputs success or failure for each.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
status = EXIT_SUCCESS;
for (arg = 1; arg < argc; arg++)
if (xdg_open(argv[arg])) {
printf("%s: %s.\n", argv[arg], strerror(errno));
status = EXIT_FAILURE;
} else
printf("%s: Opened.\n", argv[arg]);
return status;
}
As the SPDX license identifier states, this example code is licensed under Creative Commons Zero 1.0. Use it any way you wish, in any code you want.
The xdg-open is part of the xdg-utils. They're almost always installed with the GUI desktop of any Linux distribution.
A Linux distribution can be installed without any Graphical User Interface, on servers say, and most probably then they would lack xdg-open.
Instead of system, you could - and should - use fork + exec - if exec fails then xdg-open could not be executed.
The online compilers most probably don't have any Desktop GUI installed on them, thus the lack of that utility.

Open function : how to protect against directory opening?

I am using the open function like this in my file to get some coordinates from a file :
t_coo *get_buffer(char **av, t_coo **head)
{
int ret;
int fd;
char *line;
int y;
t_coo *cur;
cur = NULL;
*head = NULL;
y = 0;
ret = 0;
fd = open(av[1], O_RDONLY);
while ((ret = get_next_line(fd, &line) > 0))
{
head = get_map(line, head, y);
y++;
}
close(fd);
cur = *head;
return (cur);
}
It is working perfectly but the problem is when I try to make it open a directory, my program segfault. I want to protect it against directory opening so that I dont segault anymore. I tried to look at the flags on the internet and tried many of them but I could not find this one. Can anybody tell me which one it is? Thank you.
You need to use the lstat function to tell you whether the given file name represents a regular file or a directory.
struct stat statbuf;
int rval;
rval = lstat(argv[1], &statbuf);
if (rval == -1) {
perror("error getting file status");
exit(1);
}
if (S_ISREG(statbuf.st_mode)) {
printf("%s is a regular file\n", argv[1]);
} else if (S_ISDIR(statbuf.st_mode)) {
printf("%s is a directory\n", argv[1]);
} else {
printf("%s is something else\n", argv[1]);
}
I would suggest to open the file (which could be a directory) so get a file descriptor, then use fstat(2) on that file descriptor (and check the result of fstat by using statresult.st_mode & S_IFMT == S_IFDIR ...)
This would avoid an (improbable) race condition with the lstat (or stat) then open approach (suggested in Dbush's answer): some other process might (with very bad luck) remove or rename the file between these two system calls. You might also opendir or else open (but that also suffers from a similar race condition).
PS. The race conditions I am suggesting here are so improbable that we can normally ignore them... But they might be a security flaw (than an attacker could use)

Segmentation fault (core dumped)

I'm writing a program in c that basically copies files, but I'm getting this error: Segmentation fault (core dumped). From what I'm reading I think it's because I'm trying to access memory that hasn't been allocated yet. I'm a newbie when it comes to c and I suck at pointers, so I was wondering if you guys could tell me which pointer is causing this and how to fix it if possible. Btw, this program is supposed to be a daemon, but I haven't put anything inside the infinite while loop at the bottom.
Here is my code:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <dirent.h>
int main(int c, char *argv[]) {
char *source, *destination;
char *list1[30], *list2[30], *listDif[30];
unsigned char buffer[4096];
int i=0, x=0, sizeSource=0, sizeDest=0, sizeDif=0;
int outft, inft,fileread;
int sleeper;
struct dirent *ent, *ent1;
//Check number of arguments
if(c<3)
{
printf("Daemon wrongly called\n");
printf("How to use: <daemon name> <orginDirectory> <destinationDirectory> \n");
printf("or : <daemon name> <orginDirectory> <destinationDirectory> <sleeperTime(seconds)>");
return 0;
}
//Checks if sleeper time is given or will be the default 5minutes
/*if(c=4)
{
char *p;
errno = 0;
long conv = strtol(argv[3], &p, 10);
if(errno != 0 || *p != '\0')
{
printf("Number given for sleeper incorrect, it has to be an integer value.\n");
return(0);
} else
{
sleeper = conv;
}
} else
{
sleeper = 300;
}*/
//Get path of directories from arguments
source = argv[1];
destination = argv[2];
//Check if directories exist
DIR* dirSource = opendir(source);
if (!dirSource)
{
printf("Source directory incorrect\n");
return 0;
}
DIR* dirDest = opendir(destination);
if (!dirDest)
{
printf("Destination directory incorrect\n");
return 0;
}
/* save all the files and directories within directory */
while ((ent = readdir (dirSource)) != NULL) {
list1[sizeSource] = strdup(ent->d_name);
sizeSource++;
if(sizeSource>=30){break;}
}
closedir(dirSource);
while((ent1 = readdir (dirDest)) != NULL) {
list2[sizeDest] = strdup(ent1->d_name);
sizeDest++;
if(sizeDest>=30){break;}
}
closedir(dirDest);
/* Verify the diferences between the directories and save them */
int z;
int dif = 0; //0 - False | 1 - True
printf("Diferenças:\n");
for(i=0;i<sizeSource;i++){
dif = 0;
for(z=0;z<sizeDest;z++){
if(strcmp(list1[i],list2[z])==0){ //If there is no match, it saves the name of the file to listDif[]
dif = 1;
break;
}
}
if(dif==0) {
printf("%s\n",list1[i]);
listDif[sizeDif] = list1[i];
sizeDif++;
}
}
/* This code will copy the files */
z=0;
while(z!=sizeDif){
// output file opened or created
char *pathSource, *pathDest;
strcpy(pathSource, source);
strcat(pathSource, "/");
strcat(pathSource, listDif[z]);
strcpy(pathDest, destination);
strcat(pathDest, "/");
strcat(pathDest, listDif[z]);
// output file opened or created
if((outft = open(pathDest, O_CREAT | O_APPEND | O_RDWR))==-1){
perror("open");
}
// lets open the input file
inft = open(pathSource, O_RDONLY);
if(inft >0){ // there are things to read from the input
fileread = read(inft, buffer, sizeof(buffer));
printf("%s\n", buffer);
write(outft, buffer, fileread);
close(inft);
}
close(outft);
}
/* Our process ID and Session ID */
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
/* Open any logs here */
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}
/* Change the current working directory */
if ((chdir("/")) < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}
/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
/* Daemon-specific initialization goes here */
/* The Big Loop */
while (1) {
//sleep(5); /* wait 5 seconds */
}
exit(EXIT_SUCCESS);
}
The result of ls is:
ubuntu#ubuntu:~/Desktop$ ls
Concatenar_Strings.c core D2 daemon.c examples.desktop
Concatenar_Strings.c~ D1 daemon daemon.c~ ubiquity.desktop
D1 and D2 are folders, and in D1 are three text documents that I want to copy into D2.
One other question, is this a delayed error or an immediate one? Because I doubt this message would appear on a code line that with two integers.
Thanks in advance guys.
This loop is wrong:
while ((ent = readdir (dirSource)) != NULL) {
list1[sizeSource] = ent->d_name;
Probably, ent points to the same memory block every time, and the readdir function updates it. So when you save that pointer, you end up with your list containing invalid pointers (probably end up all pointing to the same string). Further, the string may be deallocated once you got to the end of the directory.
If you want to use the result of readdir after closing the directory or after calling readdir again you will need to take a copy of the data. In this case you can use strdup and it is usually good style to free the string at the end of the operation.
This may or may not have been the cause of your segfault. Another thing to check is that you should break out of your loops if sizeSource or sizeDest hits 30.
In the strcmp loop, you should really set dif = 0 at the start of the i loop, instead of in an else block.
Update: (more code shown by OP)
char *pathSource, *pathDest;
strcpy(pathSource, source);
You are copying to a wild pointer, which is a likely cause of segfaults. strcpy does not allocate any memory, it expects that you have already allocated enough.
One possible fix would be:
char pathSource[strlen(source) + 1 + strlen(listDif[z]) + 1];
sprintf(pathSource, "%s/%s", source, listDif[z]);
Alternatively (without using VLA):
char pathSource[MAX_PATH]; // where MAX_PATH is some large number
snprintf(pathSource, MAX_PATH, "%s/%s", source, listDif[z]);
Do the same thing for pathDest.
NB. Consider moving the closedir lines up to after the readdir loops; generally speaking you should open and close a resource as close as possible to the times you start and finish using them respectively; this makes your code easier to maintain.

How to read / write files to directories in C?

I need to write a binary file to some specified directory using a C program. But, while writing I want to make sure that if the directory structure does not exist it should create one for me.
How can I achieve this?
You have to check whether the leaf directory exists, using stat(), and iteratively create directories in a top-down manner, using mkdir(). Or you may even abandon the check and start with a directory creation - it will just fail with bunch of EEXIST errors if your directories are already there. This is just an easy way.
If you're concerned about correctness, there is a better way. You should use openat() on subsequent available components (directories) of the desired path and mkdirat() to create lacking ones. That way you can avoid possible races. In the end, you should open your file using openat() too.
Check relevant manpages to see how you should use mentioned system calls.
If the above rod was not sufficiently long enough for you, then below you can find my simple Linux-flavored fish. xopen() function allows you to open the file in a given path, creating directories along the way.
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
/* Look out! It modifies pathname! */
int xopen(char *pathname, int flags, mode_t mode, mode_t dirmode)
{
int fd, errsv;
int dirfd = AT_FDCWD;
char *ptr = pathname;
while (*ptr == '/')
ptr++;
for (;;) {
strsep(&ptr, "/");
if (ptr == NULL) {
fd = openat(dirfd, pathname, flags, mode);
break;
}
while (*ptr == '/')
ptr++;
if (*ptr == '\0') {
errno = EISDIR;
fd = -1;
break;
}
if ((fd = mkdirat(dirfd, pathname, dirmode)) < 0 && errno != EEXIST)
break;
if ((fd = openat(dirfd, pathname, O_DIRECTORY)) < 0)
break;
if (dirfd != AT_FDCWD)
close(dirfd);
dirfd = fd;
pathname = ptr;
}
errsv = errno;
if (dirfd != AT_FDCWD)
close(dirfd);
errno = errsv;
return fd;
}

Resources