1 /* @(#)at-base.c 1.2 18/07/16 Copyright 2011-2018 J. Schilling */
2 /*
3 * Emulate the behavior of openat(fd, name, flag, mode)
4 * Replace "openat" by "FUNC_CALL".
5 *
6 * Note that emulation methods that do not use the /proc filesystem are
7 * not MT safe. In the non-MT-safe case, we do:
8 *
9 * savewd()/fchdir()/open(name)/restorewd()
10 *
11 * Since the /proc method may fail with ENAMETOOLONG, we need to fall back
12 * to the fchdir() method in case of long path names and as a result are
13 * not MT-safe with long path names.
14 *
15 * Errors may force us to abort the program as our caller is not expected
16 * to know that we do more than a simple open() here and that the
17 * working directory may be changed by us.
18 *
19 * Copyright (c) 2011-2018 J. Schilling
20 */
21 /*
22 * The contents of this file are subject to the terms of the
23 * Common Development and Distribution License, Version 1.0 only
24 * (the "License"). You may not use this file except in compliance
25 * with the License.
26 *
27 * See the file CDDL.Schily.txt in this distribution for details.
28 * A copy of the CDDL is also available via the Internet at
29 * http://www.opensource.org/licenses/cddl1.txt
30 *
31 * When distributing Covered Code, include this CDDL HEADER in each
32 * file and include the License file CDDL.Schily.txt from this distribution.
33 */
34
35 #ifndef ENAMETOOLONG
36 #define ENAMETOOLONG EINVAL
37 #endif
38
39 #ifndef _AT_TRIGGER
40 #define _AT_TRIGGER 0
41 #endif
42
43 #ifdef PROTOTYPES
44 EXPORT FUNC_RESULT
FUNC_NAME(int fd,const char * name PROTO_DECL)45 FUNC_NAME(int fd, const char *name PROTO_DECL)
46 #else
47 EXPORT FUNC_RESULT
48 FUNC_NAME(fd, name KR_ARGS)
49 int fd;
50 const char *name;
51 KR_DECL
52 #endif
53 {
54 int n;
55 FUNC_RESULT ret = 0;
56 char buf[PATH_MAX];
57 char *proc_name;
58 struct save_wd save_wd;
59
60 FLAG_CHECK();
61 if (fd == AT_FDCWD || ABS_NAME(name))
62 return (FUNC_CALL(name));
63
64 if ((proc_name = proc_fd2name(buf, fd, name)) != NULL) {
65 /*
66 * The next FUNC_CALL() frequently results in ENAMETOOLONG
67 */
68 ret = FUNC_CALL(proc_name);
69 if (ret >= 0 || NON_PROCFS_ERRNO(errno))
70 return (ret);
71 }
72
73 /*
74 * /proc open failed or /proc not available on this platform.
75 * Give it a chance using fchdir(). But the following code is
76 * not MT-safe!
77 */
78
79 if (savewd(&save_wd) < 0) {
80 /*
81 * We abort here as the caller may not know that we are forced
82 * to savewd/fchdir/restorewd here and misinterpret errno.
83 */
84 savewd_abort(geterrno());
85 /* NOTREACHED */
86 return (-1);
87 }
88 if (fd >= 0 && save_wd.fd == fd) {
89 /*
90 * If we just opened "fd" with the same number in savewd(), fd
91 * must have been illegal when calling openat();
92 */
93 closewd(&save_wd);
94 seterrno(EBADF);
95 return (-1);
96 }
97 if ((n = fchdir(fd)) < 0) {
98 int err = geterrno();
99 /*
100 * In case that fchdir() is emulated via chdir() and we use a
101 * multi hop chdir(), we may be on an undefined intermediate
102 * directory. Try to return to last working directory and if
103 * this fails, abort for security.
104 */
105 if (n == -2 && restorewd(&save_wd) < 0) {
106 restorewd_abort(geterrno());
107 /* NOTREACHED */
108 }
109 closewd(&save_wd);
110 seterrno(err);
111 return (-1);
112 }
113
114 ret = FUNC_CALL(name); /* The actual call() */
115
116 if (restorewd(&save_wd) < 0) {
117 int err = geterrno();
118
119 closewd(&save_wd);
120 restorewd_abort(err);
121 /* NOTREACHED */
122 seterrno(err);
123 return (-1);
124 }
125 closewd(&save_wd);
126
127 return (ret);
128 }
129