summaryrefslogtreecommitdiffhomepage
path: root/src/internal/slibtool_realpath_impl.c
blob: e1e47b501175cc3ab0508a3c753a8dcddfe7d739 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/*******************************************************************/
/*  slibtool: a skinny libtool implementation, written in C        */
/*  Copyright (C) 2016--2024  SysDeer Technologies, LLC            */
/*  Released under the Standard MIT License; see COPYING.SLIBTOOL. */
/*******************************************************************/

#include <fcntl.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <sys/stat.h>
#include <slibtool/slibtool.h>

#include "slibtool_driver_impl.h"
#include "slibtool_readlink_impl.h"
#include "slibtool_realpath_impl.h"
#include "slibtool_visibility_impl.h"

#ifdef HAVE_SYS_SYSCALL_H
#include <sys/syscall.h>
#endif

#ifdef _MIDIPIX_ABI
#include <sys/fs.h>
#endif

#ifndef ENOTSUP
#define ENOTSUP EOPNOTSUPP
#endif

#ifdef SYS___realpathat
extern long syscall(int, ...);
#endif

slbt_hidden int slbt_realpath(
	int             fdat,
	const char *    path,
	int             options,
	char *          buf,
	size_t          buflen)
{
	int             ret;
	int             fd;
	int             fdproc;
	struct stat     st;
	struct stat     stproc;
	char    procfspath[36];

	/* common validation */
	if (!buf || (options & O_CREAT)) {
		errno = EINVAL;
		return -1;
	}

	/* framework-based wrapper */
#ifdef _MIDIPIX_ABI
	return __fs_rpath(fdat,path,options,buf,buflen);
#endif

#ifdef SYS___realpathat
	return syscall(SYS___realpathat,fdat,path,buf,buflen,0);
#endif

	/* buflen */
	if (buflen < PATH_MAX) {
		errno = ENOBUFS;
		return -1;
	}

	/* AT_FDCWD */
	if (fdat == AT_FDCWD) {
		return realpath(path,buf) ? 0 : -1;
	}

	/* /proc/self/fd */
	if ((fd = openat(fdat,path,options,0)) < 0)
		return -1;

	sprintf(procfspath,"/proc/self/fd/%d",fd);

	if (slbt_readlinkat(fdat,procfspath,buf,buflen)) {
		close(fd);
		return -1;
	}

	if ((fdproc = openat(AT_FDCWD,buf,options|O_NOFOLLOW,0)) < 0) {
		close(fd);
		errno = ELOOP;
		return -1;
	}

	ret = fstat(fd,&st) || fstat(fdproc,&stproc);

	close(fd);
	close(fdproc);

	if (ret || (st.st_dev != stproc.st_dev) || (st.st_ino != stproc.st_ino)) {
		errno = ENOTSUP;
		return -1;
	}

	return 0;
}