#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-prototypes"
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)getcwd.c 8.5 (Berkeley) 2/7/95";
#endif
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/lib/libc/gen/getcwd.c,v 1.29 2007/01/09 00:27:53 imp Exp $");
#include "namespace.h"
#include <sys/param.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "un-namespace.h"
#if TARGET_OS_OSX && !TARGET_OS_SIMULATOR
#include <sys/attr.h>
#include <apfs/apfs_fsctl.h>
#endif
#define ISDOT(dp) \
(dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
(dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
static inline int
__check_for_firmlink(char *dir_path)
{
#if TARGET_OS_OSX && !TARGET_OS_SIMULATOR
apfs_firmlink_control_t afc;
int is_firmlink;
int err;
afc.cmd = FIRMLINK_GET;
afc.val = 0;
err = fsctl(dir_path, APFSIOC_FIRMLINK_CTL, (void *)&afc, FSOPT_NOFOLLOW);
if (err == 0) {
is_firmlink = afc.val ? 1 : 0;
} else {
is_firmlink = 1;
}
return is_firmlink;
#else
return 0;
#endif
}
static inline int
__getcwd(char *buf, size_t size)
{
int fd, err, save;
struct stat dot, pt;
char *b;
if ((fd = open(".", O_RDONLY)) < 0)
return -1;
if (fstat(fd, &dot) < 0) {
save = errno;
close(fd);
errno = save;
return -1;
}
if (dot.st_dev == 0 || dot.st_ino == 0) {
close(fd);
errno = EINVAL;
return -1;
}
if (size < MAXPATHLEN) {
b = (char *)alloca(MAXPATHLEN);
if (b == NULL) {
close(fd);
errno = ENOMEM;
return -1;
}
} else
b = buf;
err = fcntl(fd, F_GETPATH, b);
if (err) {
save = errno;
close(fd);
errno = save;
return err;
}
close(fd);
if (stat(b, &pt) < 0)
return -1;
if (dot.st_dev != pt.st_dev || dot.st_ino != pt.st_ino) {
errno = EINVAL;
return -1;
}
if (size < MAXPATHLEN) {
if (strlen(b) >= size) {
errno = ERANGE;
return -1;
}
strcpy(buf, b);
}
return 0;
}
__private_extern__ char *
__private_getcwd(pt, size, usegetpath)
char *pt;
size_t size;
int usegetpath;
{
struct dirent *dp;
DIR *dir = NULL;
dev_t dev;
ino_t ino;
int first;
char *bpt, *bup;
struct stat s;
dev_t root_dev;
ino_t root_ino;
size_t ptsize, upsize;
int save_errno;
char *ept, *eup, *up;
if (pt) {
ptsize = 0;
if (!size) {
errno = EINVAL;
return (NULL);
}
if (size == 1) {
errno = ERANGE;
return (NULL);
}
ept = pt + size;
} else {
if ((pt = malloc(ptsize = MAXPATHLEN)) == NULL)
return (NULL);
ept = pt + ptsize;
}
if (usegetpath) {
if (__getcwd(pt, ept - pt) == 0) {
return (pt);
} else if (errno == ERANGE)
return NULL;
}
bpt = ept - 1;
*bpt = '\0';
if ((up = malloc(upsize = MAXPATHLEN)) == NULL)
goto err;
eup = up + MAXPATHLEN;
bup = up;
up[0] = '.';
up[1] = '\0';
if (stat("/", &s))
goto err;
root_dev = s.st_dev;
root_ino = s.st_ino;
errno = 0;
for (first = 1;; first = 0) {
if (lstat(up, &s))
goto err;
ino = s.st_ino;
dev = s.st_dev;
if (root_dev == dev && root_ino == ino) {
*--bpt = '/';
bcopy(bpt, pt, ept - bpt);
free(up);
return (pt);
}
while (bup + 3 + MAXNAMLEN + 1 >= eup) {
if ((up = reallocf(up, upsize *= 2)) == NULL)
goto err;
bup = up;
eup = up + upsize;
}
*bup++ = '.';
*bup++ = '.';
*bup = '\0';
if (!(dir = opendir(up)) || _fstat(dirfd(dir), &s))
goto err;
*bup++ = '/';
*bup = '\0';
save_errno = 0;
if (s.st_dev == dev) {
for (;;) {
if (!(dp = readdir(dir)))
goto notfound;
if (dp->d_fileno == ino) {
break;
} else if (!ISDOT(dp) && dp->d_type == DT_DIR) {
bcopy(dp->d_name, bup, dp->d_namlen + 1);
if (__check_for_firmlink(up) == 0)
continue;
if (lstat(up, &s)) {
if (!save_errno)
save_errno = errno;
errno = 0;
continue;
}
if (s.st_dev == dev && s.st_ino == ino)
break;
}
}
} else
for (;;) {
if (!(dp = readdir(dir)))
goto notfound;
if (ISDOT(dp))
continue;
bcopy(dp->d_name, bup, dp->d_namlen + 1);
if (lstat(up, &s)) {
if (!save_errno)
save_errno = errno;
errno = 0;
continue;
}
if (s.st_dev == dev && s.st_ino == ino)
break;
}
while (bpt - pt < dp->d_namlen + (first ? 1 : 2)) {
size_t len, off;
if (!ptsize) {
errno = ERANGE;
goto err;
}
off = bpt - pt;
len = ept - bpt;
if ((pt = reallocf(pt, ptsize *= 2)) == NULL)
goto err;
bpt = pt + off;
ept = pt + ptsize;
bcopy(bpt, ept - len, len);
bpt = ept - len;
}
if (!first)
*--bpt = '/';
bpt -= dp->d_namlen;
bcopy(dp->d_name, bpt, dp->d_namlen);
(void) closedir(dir);
dir = NULL;
*bup = '\0';
}
notfound:
if (!errno)
errno = save_errno ? save_errno : ENOENT;
err:
save_errno = errno;
if (ptsize)
free(pt);
if (dir)
(void) closedir(dir);
free(up);
errno = save_errno;
return (NULL);
}
char *
getcwd(pt, size)
char *pt;
size_t size;
{
return __private_getcwd(pt, size, 1);
}
#pragma clang diagnostic pop