1 /* @(#)at-base2.c	1.2 18/07/16 Copyright 2011-2018 J. Schilling */
2 /*
3  *	Emulate the behavior of FUNC_NAME(fd1, name1, fd2, name2, flag)
4  *
5  *	Note that emulation methods that do not use the /proc filesystem are
6  *	not MT safe. In the non-MT-safe case, we do:
7  *
8  *		savewd()/fchdir()/open(name)/restorewd()
9  *
10  *	Since the /proc method may fail with ENAMETOOLONG, we need to fall back
11  *	to the fchdir() method in case of long path names and as a result are
12  *	not MT-safe with long path names.
13  *
14  *	Errors may force us to abort the program as our caller is not expected
15  *	to know that we do more than a simple open() here and that the
16  *	working directory may be changed by us.
17  *
18  *	Copyright (c) 2011-2018 J. Schilling
19  */
20 /*
21  * The contents of this file are subject to the terms of the
22  * Common Development and Distribution License, Version 1.0 only
23  * (the "License").  You may not use this file except in compliance
24  * with the License.
25  *
26  * See the file CDDL.Schily.txt in this distribution for details.
27  * A copy of the CDDL is also available via the Internet at
28  * http://www.opensource.org/licenses/cddl1.txt
29  *
30  * When distributing Covered Code, include this CDDL HEADER in each
31  * file and include the License file CDDL.Schily.txt from this distribution.
32  */
33 
34 #ifndef	ENAMETOOLONG
35 #define	ENAMETOOLONG	EINVAL
36 #endif
37 
38 EXPORT FUNC_RESULT
39 FUNC_NAME(fd1, name1, fd2, name2 KR_ARGS)
40 	int		fd1;
41 	const char	*name1;
42 	int		fd2;
43 	const char	*name2;
44 	KR_DECL
45 {
46 	int	n;
47 	BOOL	same_fd =  FALSE;
48 	FUNC_RESULT	ret = 0;
49 	char	buf1[PATH_MAX];
50 	char	buf2[PATH_MAX];
51 	char	*proc_name1;
52 	char	*proc_name2;
53 	struct save_wd save_wd;
54 
55 	FLAG_CHECK();
56 
57 /*
58  * There are 17 cases to implement:
59  *
60  * case	fd1	name1	 fd2	name2
61  * 1	AT_CWD	absolute AT_CWD	absolute   call xxx(name1, name2)
62  * 2	AT_CWD	absolute AT_CWD	relative   call xxx(name1, name2)
63  * 3	AT_CWD	relative AT_CWD	absolute   call xxx(name1, name2)
64  * 4	AT_CWD	relative AT_CWD	relative   call xxx(name1, name2)
65  * 5	AT_CWD	absolute fd	absolute   call xxx(name1, name2)
66  * 6	AT_CWD	relative fd	absolute   call xxx(name1, name2)
67  * 7	fd	absolute AT_CWD	absolute   call xxx(name1, name2)
68  * 8	fd	absolute AT_CWD	relative   call xxx(name1, name2)
69  * 9	fd	absolute fd	absolute   call xxx(name1, name2)
70  *
71  * 10	AT_CWD	absolute fd	relative   chdir to fd2; call xxx(name1, name2)
72  * 11	AT_CWD	relative fd	relative   convert name1 to abs; case 10
73  * 12	fd	relative AT_CWD	absolute   chdir to fd1; call xxx(name1, name2)
74  * 13	fd	relative AT_CWD	relative   convert name2 to abs; case 12
75  *
76  * 14	fd	absolute fd	relative   chdir to fd2; call xxx(name1, name2)
77  * 15	fd	relative fd	absolute   chdir to fd1; call xxx(name1, name2)
78  *
79  * 16	fd1	relative fd1	relative   chdir to fd1; call xxx(name1, name2)
80  * 17	fd	relative fd	relative   chdir to fd1; case 11
81  */
82 
83 	/*
84 	 * Case 1..9
85 	 */
86 	if ((fd1 == AT_FDCWD || ABS_NAME(name1)) &&
87 	    (fd2 == AT_FDCWD || ABS_NAME(name2)))
88 		return (FUNC_CALL(name1, name2));
89 
90 	/*
91 	 * If the procfs interface works, we can catch all other cases here:
92 	 */
93 	if ((proc_name1 = proc_fd2name(buf1, fd1, name1)) != NULL &&
94 	    (proc_name2 = proc_fd2name(buf2, fd2, name2)) != NULL) {
95 		/*
96 		 * The next FUNC_CALL() frequently results in ENAMETOOLONG
97 		 */
98 		ret = FUNC_CALL(proc_name1, proc_name2);
99 		if (ret >= 0 || NON_PROCFS_ERRNO(errno))
100 			return (ret);
101 	}
102 
103 	/*
104 	 * /proc open failed or /proc not available on this platform.
105 	 * Give it a chance using fchdir(). But the following code is
106 	 * not MT-safe!
107 	 */
108 
109 	if (savewd(&save_wd) < 0) {
110 		/*
111 		 * We abort here as the caller may not know that we are forced
112 		 * to savewd/fchdir/restorewd here and misinterpret errno.
113 		 */
114 		savewd_abort(geterrno());
115 		/* NOTREACHED */
116 		return (-1);
117 	}
118 	if ((fd1 >= 0 && save_wd.fd == fd1) ||
119 	    (fd2 >= 0 && save_wd.fd == fd2)) {
120 		/*
121 		 * If we just opened "fd" with the same number in savewd(),
122 		 * fd1/fd2 must have been illegal when calling us();
123 		 */
124 		closewd(&save_wd);
125 		seterrno(EBADF);
126 		return (-1);
127 	}
128 	if (!ABS_NAME(name1) && !ABS_NAME(name2)) {
129 		/*
130 		 * Check for case 16
131 		 */
132 		if (fd1 == fd2) {
133 			same_fd = TRUE;
134 		}
135 #ifndef	_MSC_VER
136 		/*
137 		 * Are there other non-cooperative platforms where stat()
138 		 * does not allow to compare files?
139 		 */
140 		else {
141 			struct stat sb1;
142 			struct stat sb2;
143 
144 			if (fstat(fd1, &sb1) >= 0 &&
145 			    fstat(fd2, &sb2) >= 0) {
146 				if (sb1.st_dev == sb2.st_dev &&
147 				    sb1.st_ino == sb2.st_ino)
148 					same_fd = TRUE;
149 			}
150 		}
151 #endif
152 	}
153 case_10_12:
154 	if (ABS_NAME(name1) || ABS_NAME(name2) || same_fd) {
155 		/*
156 		 * Handle case 10, 12, 14, 15, 16
157 		 */
158 		int	fd;
159 
160 		if (ABS_NAME(name1))
161 			fd = fd2;
162 		else
163 			fd = fd1;
164 		if ((n = fchdir(fd)) < 0) {
165 			int	err = geterrno();
166 			/*
167 			 * In case that fchdir() is emulated via chdir()
168 			 * and we use a multi hop chdir(), we may be on
169 			 * an undefined intermediate directory. Try to
170 			 * return to last working directory and if this
171 			 * fails, abort for security.
172 			 */
173 			if (n == -2 && restorewd(&save_wd) < 0) {
174 				restorewd_abort(geterrno());
175 				/* NOTREACHED */
176 			}
177 			closewd(&save_wd);
178 			seterrno(err);
179 			return (-1);
180 		}
181 	} else {
182 		/*
183 		 * Handle case 11, 13, 17
184 		 */
185 		if (fd1 == AT_FDCWD) {
186 case_11:
187 			/*
188 			 * Existing file: follow all but last component, abort
189 			 * if the file does not exist.
190 			 */
191 			if (absfpath(name1, buf1, sizeof (buf1),
192 				    RSPF_EXIST | RSPF_NOFOLLOW_LAST) == NULL) {
193 				ret = -1;
194 				goto fail;
195 			}
196 			name1 = buf1;
197 			goto case_10_12;
198 		} else if (fd2 == AT_FDCWD) {
199 			/*
200 			 * New file: may not exist,
201 			 * use absnpath() -> absfpath(... 0)
202 			 */
203 			if (absnpath(name2, buf2, sizeof (buf2)) == NULL) {
204 				ret = -1;
205 				goto fail;
206 			}
207 			name2 = buf2;
208 			goto case_10_12;
209 		} else {
210 			if ((n = fchdir(fd1)) < 0) {
211 				int	err = geterrno();
212 				/*
213 				 * In case that fchdir() is emulated via chdir()
214 				 * and we use a multi hop chdir(), we may be on
215 				 * an undefined intermediate directory. Try to
216 				 * return to last working directory and if this
217 				 * fails, abort for security.
218 				 */
219 				if (n == -2 && restorewd(&save_wd) < 0) {
220 					restorewd_abort(geterrno());
221 					/* NOTREACHED */
222 				}
223 				closewd(&save_wd);
224 				seterrno(err);
225 				return (-1);
226 			}
227 			goto case_11;
228 		}
229 	}
230 
231 	ret = FUNC_CALL(name1, name2);		/* The actual call() */
232 
233 fail:
234 	if (restorewd(&save_wd) < 0) {
235 		int	err = geterrno();
236 
237 		closewd(&save_wd);
238 		restorewd_abort(err);
239 		/* NOTREACHED */
240 		seterrno(err);
241 		return (-1);
242 	}
243 	closewd(&save_wd);
244 
245 	return (ret);
246 }
247