1 
2 /*******************************************************************/
3 /*  slibtool: a skinny libtool implementation, written in C        */
4 /*  Copyright (C) 2016--2020  Z. Gilboa                            */
5 /*  Released under the Standard MIT License; see COPYING.SLIBTOOL. */
6 /*******************************************************************/
7 
8 #include <fcntl.h>
9 #include <stdlib.h>
10 #include <limits.h>
11 #include <errno.h>
12 #include <sys/stat.h>
13 #include <slibtool/slibtool.h>
14 
15 #include "slibtool_driver_impl.h"
16 #include "slibtool_readlink_impl.h"
17 
18 #ifdef __unix__
19 #include <sys/syscall.h>
20 #endif
21 
22 #ifdef _MIDIPIX_ABI
23 #include <sys/fs.h>
24 #endif
25 
26 #ifndef ENOTSUP
27 #define ENOTSUP EOPNOTSUPP
28 #endif
29 
30 #ifdef SYS___realpathat
31 extern long syscall(int, ...);
32 #endif
33 
slbt_realpath(int fdat,const char * path,int options,char * buf,size_t buflen)34 int slbt_realpath(
35 	int             fdat,
36 	const char *    path,
37 	int             options,
38 	char *          buf,
39 	size_t          buflen)
40 {
41 	int             ret;
42 	int             fd;
43 	int             fdproc;
44 	struct stat     st;
45 	struct stat     stproc;
46 	char    procfspath[36];
47 
48 	/* common validation */
49 	if (!buf || (options & O_CREAT)) {
50 		errno = EINVAL;
51 		return -1;
52 	}
53 
54 	/* framework-based wrapper */
55 #ifdef _MIDIPIX_ABI
56 	return __fs_rpath(fdat,path,options,buf,buflen);
57 #endif
58 
59 #ifdef SYS___realpathat
60 	return syscall(SYS___realpathat,fdat,path,buf,buflen,0);
61 #endif
62 
63 	/* buflen */
64 	if (buflen < PATH_MAX) {
65 		errno = ENOBUFS;
66 		return -1;
67 	}
68 
69 	/* AT_FDCWD */
70 	if (fdat == AT_FDCWD) {
71 		return realpath(path,buf) ? 0 : -1;
72 	}
73 
74 	/* /proc/self/fd */
75 	if ((fd = openat(fdat,path,options,0)) < 0)
76 		return -1;
77 
78 	sprintf(procfspath,"/proc/self/fd/%d",fd);
79 
80 	if (slbt_readlinkat(fdat,procfspath,buf,buflen)) {
81 		close(fd);
82 		return -1;
83 	}
84 
85 	if ((fdproc = openat(AT_FDCWD,buf,options|O_NOFOLLOW,0)) < 0) {
86 		close(fd);
87 		errno = ELOOP;
88 		return -1;
89 	}
90 
91 	ret = fstat(fd,&st) || fstat(fdproc,&stproc);
92 
93 	close(fd);
94 	close(fdproc);
95 
96 	if (ret || (st.st_dev != stproc.st_dev) || (st.st_ino != stproc.st_ino)) {
97 		errno = ENOTSUP;
98 		return -1;
99 	}
100 
101 	return 0;
102 }
103