Blame src/internal/slibtool_realpath_impl.c

d4b2a5
/*******************************************************************/
d4b2a5
/*  slibtool: a skinny libtool implementation, written in C        */
d4b2a5
/*  Copyright (C) 2016--2024  SysDeer Technologies, LLC            */
d4b2a5
/*  Released under the Standard MIT License; see COPYING.SLIBTOOL. */
d4b2a5
/*******************************************************************/
d4b2a5
d4b2a5
#include <fcntl.h>
d4b2a5
#include <stdlib.h>
d4b2a5
#include <limits.h>
d4b2a5
#include <errno.h>
d4b2a5
#include <sys/stat.h>
d4b2a5
#include <slibtool/slibtool.h>
d4b2a5
d4b2a5
#include "slibtool_driver_impl.h"
d4b2a5
#include "slibtool_readlink_impl.h"
d4b2a5
#include "slibtool_realpath_impl.h"
d4b2a5
#include "slibtool_visibility_impl.h"
d4b2a5
d4b2a5
#ifdef HAVE_SYS_SYSCALL_H
d4b2a5
#include <sys/syscall.h>
d4b2a5
#endif
d4b2a5
d4b2a5
#ifdef _MIDIPIX_ABI
d4b2a5
#include <sys/fs.h>
d4b2a5
#endif
d4b2a5
d4b2a5
#ifndef ENOTSUP
d4b2a5
#define ENOTSUP EOPNOTSUPP
d4b2a5
#endif
d4b2a5
d4b2a5
#ifdef SYS___realpathat
d4b2a5
extern long syscall(int, ...);
d4b2a5
#endif
d4b2a5
d4b2a5
slbt_hidden int slbt_realpath(
d4b2a5
	int             fdat,
d4b2a5
	const char *    path,
d4b2a5
	int             options,
d4b2a5
	char *          buf,
d4b2a5
	size_t          buflen)
d4b2a5
{
d4b2a5
	int             ret;
d4b2a5
	int             fd;
d4b2a5
	int             fdproc;
d4b2a5
	struct stat     st;
d4b2a5
	struct stat     stproc;
d4b2a5
	char    procfspath[36];
d4b2a5
d4b2a5
	/* common validation */
d4b2a5
	if (!buf || (options & O_CREAT)) {
d4b2a5
		errno = EINVAL;
d4b2a5
		return -1;
d4b2a5
	}
d4b2a5
d4b2a5
	/* framework-based wrapper */
d4b2a5
#ifdef _MIDIPIX_ABI
d4b2a5
	return __fs_rpath(fdat,path,options,buf,buflen);
d4b2a5
#endif
d4b2a5
d4b2a5
#ifdef SYS___realpathat
d4b2a5
	return syscall(SYS___realpathat,fdat,path,buf,buflen,0);
d4b2a5
#endif
d4b2a5
d4b2a5
	/* buflen */
d4b2a5
	if (buflen < PATH_MAX) {
d4b2a5
		errno = ENOBUFS;
d4b2a5
		return -1;
d4b2a5
	}
d4b2a5
d4b2a5
	/* AT_FDCWD */
d4b2a5
	if (fdat == AT_FDCWD) {
d4b2a5
		return realpath(path,buf) ? 0 : -1;
d4b2a5
	}
d4b2a5
d4b2a5
	/* /proc/self/fd */
d4b2a5
	if ((fd = openat(fdat,path,options,0)) < 0)
d4b2a5
		return -1;
d4b2a5
d4b2a5
	sprintf(procfspath,"/proc/self/fd/%d",fd);
d4b2a5
d4b2a5
	if (slbt_readlinkat(fdat,procfspath,buf,buflen)) {
d4b2a5
		close(fd);
d4b2a5
		return -1;
d4b2a5
	}
d4b2a5
d4b2a5
	if ((fdproc = openat(AT_FDCWD,buf,options|O_NOFOLLOW,0)) < 0) {
d4b2a5
		close(fd);
d4b2a5
		errno = ELOOP;
d4b2a5
		return -1;
d4b2a5
	}
d4b2a5
d4b2a5
	ret = fstat(fd,&st) || fstat(fdproc,&stproc);
d4b2a5
d4b2a5
	close(fd);
d4b2a5
	close(fdproc);
d4b2a5
d4b2a5
	if (ret || (st.st_dev != stproc.st_dev) || (st.st_ino != stproc.st_ino)) {
d4b2a5
		errno = ENOTSUP;
d4b2a5
		return -1;
d4b2a5
	}
d4b2a5
d4b2a5
	return 0;
d4b2a5
}