1 /* @(#)procnameat.c	1.3 18/07/16 Copyright 2011-2018 J. Schilling */
2 /*
3  *	Return a path name for a /proc related access to fd/name if possible.
4  *
5  *	We need to test this at runtime in order to avoid incorrect behavior
6  *	from running a program on a newer OS that it has been compiled for.
7  *	There are also platforms like FreeBSD where mounting /proc is optioonal.
8  *
9  **************
10  *	NOTE:	The entries /proc/self/{fd|path}/%d are symlinks and thus do
11  *		not allow to access paths of unlimited length as possible
12  *		with a real openat(fd, name, flags) call.
13  **************
14  *
15  *	If our caller falls back to the fchdir() method, e.g. because we
16  *	did not detect /proc, or because /proc/self/{fd|path}/%d results
17  *	in ENAMETOOLONG, the resulting code is no longer MT-safe.
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 #include <schily/unistd.h>
36 #include <schily/types.h>
37 #include <schily/fcntl.h>
38 #include <schily/maxpath.h>
39 #include <schily/errno.h>
40 #include <schily/standard.h>
41 #include <schily/schily.h>
42 #include "at-defs.h"
43 
44 #ifndef	ENAMETOOLONG
45 #define	ENAMETOOLONG	EINVAL
46 #endif
47 
48 #define	PROC_SELF_PATH_FORMAT	"/proc/self/path/%d/%s"	/* Newer procfs */
49 #define	PROC_SELF_FD_FORMAT	"/proc/self/fd/%d/%s"	/* Older procfs */
50 #define	PROC_PID_FD_FORMAT	"/proc/%ld/fd/%d/%s"	/* AIX has no self */
51 
52 #define	PT_SELF_PATH		1
53 #define	PT_SELF_FD		2
54 #define	PT_PID_FD		3
55 
56 char *
proc_fd2name(buf,fd,name)57 proc_fd2name(buf, fd, name)
58 	char		*buf;
59 	int		fd;
60 	const char	*name;
61 {
62 static	int	proc_type;
63 
64 	if (proc_type == 0) {
65 		int	proc_fd;
66 
67 		/*
68 		 * First test the newer feature as the older /proc/self/fd/%d
69 		 * feature is still available on newer systems.
70 		 */
71 		proc_fd = open("/proc/self/path", O_SEARCH);
72 		if (proc_fd >= 0) {
73 			proc_type = PT_SELF_PATH;
74 			close(proc_fd);
75 		} else {
76 			proc_fd = open("/proc/self/fd", O_SEARCH);
77 			if (proc_fd >= 0) {
78 				proc_type = PT_SELF_FD;
79 				close(proc_fd);
80 			} else {
81 				js_snprintf(buf, PATH_MAX,
82 					"/proc/%ld/fd", (long)getpid());
83 				proc_fd = open(buf, O_SEARCH);
84 				if (proc_fd >= 0) {
85 					proc_type = PT_PID_FD;
86 					close(proc_fd);
87 				} else {
88 					/*
89 					 * No /proc fs found
90 					 */
91 					proc_type = -1;
92 					seterrno(0);
93 					return ((char *)0);
94 				}
95 			}
96 		}
97 	} else if (proc_type < 0) {
98 		/*
99 		 * No /proc fs found
100 		 */
101 		seterrno(0);
102 		return ((char *)0);
103 	}
104 	if (proc_type == PT_PID_FD) {
105 		if (js_snprintf(buf, PATH_MAX,
106 				PROC_PID_FD_FORMAT,
107 				(long)getpid(), fd, name) >= PATH_MAX) {
108 			seterrno(ENAMETOOLONG);
109 			return (NULL);
110 		}
111 	} else if (js_snprintf(buf, PATH_MAX,
112 			proc_type == PT_SELF_PATH ?
113 				PROC_SELF_PATH_FORMAT :
114 				PROC_SELF_FD_FORMAT,
115 			fd, name) >= PATH_MAX) {
116 		seterrno(ENAMETOOLONG);
117 		return (NULL);
118 	}
119 	return (buf);
120 }
121