Skip to content

Commit 417797d

Browse files
committed
Support dirfd relative iops on Windows
We always favor calling functions like openat(), fstatat(), etc. because Linux, XNU, FreeBSD, and OpenBSD all elected to support them, while some systems like Android love them so much, that they stopped supporting the old interfaces. This change ensures that when dirfd is actually a dirfd and not AT_FDCWD we'll do the right thing on Windows NT. We use an API that's been around since Vista to accomplish that. This change also adds exponential backoff to chdir() on Windows since it seems almost as flaky on Windows 7 as the rmdir() function.
1 parent b8d26e2 commit 417797d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+361
-241
lines changed

libc/calls/access.c

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/calls/calls.h"
20-
#include "libc/calls/internal.h"
21-
#include "libc/dce.h"
2220
#include "libc/sysv/consts/at.h"
23-
#include "libc/sysv/errfuns.h"
2421

2522
/**
2623
* Checks if effective user can access path in particular ways.
@@ -31,12 +28,5 @@
3128
* @asyncsignalsafe
3229
*/
3330
int access(const char *path, int mode) {
34-
char16_t path16[PATH_MAX];
35-
if (!path) return efault();
36-
if (!IsWindows()) {
37-
return faccessat$sysv(AT_FDCWD, path, mode, 0);
38-
} else {
39-
if (__mkntpath(path, path16) == -1) return -1;
40-
return ntaccesscheck(path16, mode);
41-
}
31+
return faccessat(AT_FDCWD, path, mode, 0);
4232
}

libc/calls/calls.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ long ptrace(int, int, void *, void *);
230230
int chroot(const char *);
231231
int prctl();
232232
int sysctl(const int *, unsigned, void *, size_t *, void *, size_t);
233+
int fchdir(int);
233234

234235
#define getcwd(BUF, SIZE) \
235236
(__builtin_constant_p(BUF) && (&(BUF)[0] == NULL) ? get_current_dir_name() \

libc/calls/chdir-nt.c

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,39 @@
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/calls/internal.h"
20+
#include "libc/nt/errors.h"
2021
#include "libc/nt/files.h"
2122
#include "libc/nt/runtime.h"
23+
#include "libc/nt/synchronization.h"
2224
#include "libc/sysv/errfuns.h"
2325

2426
textwindows int chdir$nt(const char *path) {
25-
int len;
27+
int e, ms, len;
2628
char16_t path16[PATH_MAX];
2729
if ((len = __mkntpath(path, path16)) == -1) return -1;
2830
if (path16[len - 1] != u'\\') {
2931
if (len + 1 + 1 > PATH_MAX) return enametoolong();
3032
path16[len + 0] = u'\\';
3133
path16[len + 1] = u'\0';
3234
}
33-
if (SetCurrentDirectory(path16)) {
34-
return 0;
35-
} else {
36-
return __winerr();
35+
/*
36+
* chdir() seems flaky on windows 7
37+
* in a similar way to rmdir() sigh
38+
*/
39+
for (ms = 1;; ms *= 2) {
40+
if (SetCurrentDirectory(path16)) {
41+
return 0;
42+
} else {
43+
e = GetLastError();
44+
if (ms <= 512 &&
45+
(e == kNtErrorFileNotFound || e == kNtErrorAccessDenied)) {
46+
Sleep(ms);
47+
continue;
48+
} else {
49+
break;
50+
}
51+
}
3752
}
53+
errno = e;
54+
return -1;
3855
}

libc/calls/chdir.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@
2323

2424
/**
2525
* Sets current directory.
26+
*
2627
* @asyncsignalsafe
27-
* @syscall
28+
* @see fchdir()
2829
*/
2930
int chdir(const char *path) {
30-
if (!path) return efault();
3131
if (!IsWindows()) {
3232
return chdir$sysv(path);
3333
} else {

libc/calls/chown.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@
3232
* @see /etc/passwd for user ids
3333
* @see /etc/group for group ids
3434
* @asyncsignalsafe
35-
* @syscall
3635
*/
3736
int chown(const char *pathname, uint32_t uid, uint32_t gid) {
38-
if (!pathname) return efault();
3937
return fchownat$sysv(AT_FDCWD, pathname, uid, gid, 0);
4038
}

libc/calls/faccessat-nt.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222

2323
int faccessat$nt(int dirfd, const char *path, int mode, uint32_t flags) {
2424
char16_t path16[PATH_MAX];
25-
if (dirfd != AT_FDCWD || flags) return einval();
26-
if (__mkntpath(path, path16) == -1) return -1;
25+
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
2726
return ntaccesscheck(path16, mode);
2827
}

libc/calls/faccessat.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@
2525
/**
2626
* Checks if effective user can access path in particular ways.
2727
*
28-
* @param dirfd is usually AT_FDCWD
28+
* @param dirfd is normally AT_FDCWD but if it's an open directory and
29+
* file is a relative path, then file is opened relative to dirfd
2930
* @param path is a filename or directory
30-
* @param flags can be R_OK, W_OK, X_OK, F_OK
31+
* @param mode can be R_OK, W_OK, X_OK, F_OK
32+
* @param flags should be 0
3133
* @return 0 if ok, or -1 and sets errno
3234
* @asyncsignalsafe
33-
* @syscall
3435
*/
3536
int faccessat(int dirfd, const char *path, int mode, uint32_t flags) {
3637
if (!path) return efault();

libc/calls/fchdir-nt.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
2+
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
3+
╞══════════════════════════════════════════════════════════════════════════════╡
4+
│ Copyright 2020 Justine Alexandra Roberts Tunney │
5+
│ │
6+
│ Permission to use, copy, modify, and/or distribute this software for │
7+
│ any purpose with or without fee is hereby granted, provided that the │
8+
│ above copyright notice and this permission notice appear in all copies. │
9+
│ │
10+
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
11+
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
12+
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
13+
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
14+
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
15+
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
16+
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
17+
│ PERFORMANCE OF THIS SOFTWARE. │
18+
╚─────────────────────────────────────────────────────────────────────────────*/
19+
#include "libc/calls/calls.h"
20+
#include "libc/calls/internal.h"
21+
#include "libc/dce.h"
22+
#include "libc/nt/files.h"
23+
#include "libc/sysv/errfuns.h"
24+
25+
textwindows int fchdir$nt(int dirfd) {
26+
uint32_t len;
27+
char16_t dir[PATH_MAX];
28+
if (!__isfdkind(dirfd, kFdFile)) return ebadf();
29+
len = GetFinalPathNameByHandle(g_fds.p[dirfd].handle, dir, ARRAYLEN(dir),
30+
kNtFileNameNormalized | kNtVolumeNameDos);
31+
if (len + 1 + 1 > ARRAYLEN(dir)) return enametoolong();
32+
if (dir[len - 1] != u'\\') {
33+
dir[len + 0] = u'\\';
34+
dir[len + 1] = u'\0';
35+
}
36+
if (SetCurrentDirectory(dir)) {
37+
return 0;
38+
} else {
39+
return __winerr();
40+
}
41+
}

libc/calls/lstat-nt.c renamed to libc/calls/fchdir.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,18 @@
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/calls/calls.h"
2020
#include "libc/calls/internal.h"
21+
#include "libc/dce.h"
2122

22-
textwindows int lstat$nt(const char *pathname, struct stat *st) {
23-
return stat$nt(pathname, st); /* todo(jart) */
23+
/**
24+
* Sets current directory based on file descriptor.
25+
*
26+
* @see open(path, O_DIRECTORY)
27+
* @asyncsignalsafe
28+
*/
29+
int fchdir(int dirfd) {
30+
if (!IsWindows()) {
31+
return fchdir$sysv(dirfd);
32+
} else {
33+
return fchdir$nt(dirfd);
34+
}
2435
}

libc/calls/fchownat.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,8 @@
3131
* @see /etc/passwd for user ids
3232
* @see /etc/group for group ids
3333
* @asyncsignalsafe
34-
* @syscall
3534
*/
3635
int fchownat(int dirfd, const char *pathname, uint32_t uid, uint32_t gid,
3736
uint32_t flags) {
38-
/* TODO(jart): Windows? */
3937
return fchownat$sysv(dirfd, pathname, uid, gid, flags);
4038
}

0 commit comments

Comments
 (0)