Blame src/internal/slibtool_symlink_impl.c

6529aa
/*******************************************************************/
eac61a
/*  slibtool: a strong libtool implementation, written in C        */
49181b
/*  Copyright (C) 2016--2024  SysDeer Technologies, LLC            */
6529aa
/*  Released under the Standard MIT License; see COPYING.SLIBTOOL. */
6529aa
/*******************************************************************/
6529aa
6529aa
#include <stdio.h>
6529aa
#include <string.h>
6529aa
#include <stdbool.h>
6529aa
#include <unistd.h>
6529aa
b8d3bb
#include "slibtool_driver_impl.h"
d56ead
#include "slibtool_errinfo_impl.h"
6529aa
#include "slibtool_symlink_impl.h"
2baf1c
#include "slibtool_readlink_impl.h"
44fa06
#include "slibtool_realpath_impl.h"
19022e
#include "slibtool_snprintf_impl.h"
4b56de
#include "slibtool_visibility_impl.h"
6529aa
ec5e56
#define SLBT_DEV_NULL_FLAGS	(SLBT_DRIVER_ALL_STATIC      \
ec5e56
				| SLBT_DRIVER_DISABLE_SHARED \
ec5e56
				| SLBT_DRIVER_DISABLE_STATIC)
d1e257
44fa06
slbt_hidden int slbt_create_symlink_ex(
6529aa
	const struct slbt_driver_ctx *	dctx,
6529aa
	struct slbt_exec_ctx *		ectx,
44fa06
	int                             fddst,
6529aa
	const char *			target,
e0a045
	const char *			lnkname,
cc0827
	uint32_t			options)
6529aa
{
cc0827
	int		fliteral;
cc0827
	int		fwrapper;
c7981c
	int		fdevnull;
44fa06
	size_t          slen;
660491
	char **		oargv;
6529aa
	const char *	slash;
6529aa
	char *		ln[5];
9c46bd
	char *          dot;
6529aa
	char *		dotdot;
44fa06
	char *          mark;
6529aa
	char		tmplnk [PATH_MAX];
e0a045
	char		lnkarg [PATH_MAX];
2baf1c
	char		alnkarg[PATH_MAX];
6529aa
	char		atarget[PATH_MAX];
2baf1c
	char *		suffix = 0;
6529aa
cc0827
	/* options */
cc0827
	fliteral = (options & SLBT_SYMLINK_LITERAL);
cc0827
	fwrapper = (options & SLBT_SYMLINK_WRAPPER);
c7981c
	fdevnull = (options & SLBT_SYMLINK_DEVNULL);
cc0827
2baf1c
	/* symlink is a placeholder? */
93e38b
	if (fliteral) {
c7981c
		slash = target;
c7981c
93e38b
	/* .disabled .so or .a file */
93e38b
	} else if (fdevnull) {
2baf1c
		slash  = target;
2baf1c
		suffix = ".disabled";
2baf1c
2baf1c
	/* symlink target contains a dirname? */
2baf1c
	} else if ((slash = strrchr(target,'/'))) {
6529aa
		slash++;
2baf1c
2baf1c
	/* symlink target is a basename */
2baf1c
	} else {
6529aa
		slash = target;
2baf1c
	}
6529aa
2baf1c
	/* .la wrapper? */
cc0827
	dotdot = fwrapper ? "../" : "";
6529aa
2baf1c
	/* atarget */
19022e
	if (slbt_snprintf(atarget,sizeof(atarget),
19022e
			"%s%s",dotdot,slash) < 0)
d56ead
		return SLBT_BUFFER_ERROR(dctx);
6529aa
6529aa
	/* tmplnk */
19022e
	if (slbt_snprintf(tmplnk,sizeof(tmplnk),
19022e
			"%s.symlink.tmp",
19022e
			lnkname) <0)
d56ead
		return SLBT_BUFFER_ERROR(dctx);
6529aa
2baf1c
	/* placeholder? */
9c46bd
	if (fdevnull) {
44fa06
		if (unlinkat(fddst,lnkname,0) && (errno != ENOENT))
a06474
			return SLBT_SYSTEM_ERROR(dctx,0);
a06474
9c46bd
		if ((dot = strrchr(lnkname,'.'))) {
9c46bd
			if (!strcmp(dot,dctx->cctx->settings.dsosuffix)) {
9c46bd
				strcpy(dot,".expsyms.a");
9c46bd
44fa06
				if (unlinkat(fddst,lnkname,0) && (errno != ENOENT))
9c46bd
					return SLBT_SYSTEM_ERROR(dctx,0);
9c46bd
9c46bd
				strcpy(dot,dctx->cctx->settings.dsosuffix);
9c46bd
			}
9c46bd
		}
9c46bd
	}
9c46bd
2baf1c
	if (suffix) {
2baf1c
		sprintf(alnkarg,"%s%s",lnkname,suffix);
2baf1c
		lnkname = alnkarg;
2baf1c
	}
2baf1c
e0a045
	/* lnkarg */
44fa06
	if (fddst == slbt_driver_fdcwd(dctx)) {
44fa06
		strcpy(lnkarg,lnkname);
44fa06
	} else {
44fa06
		if (slbt_realpath(fddst,".",0,lnkarg,sizeof(lnkarg)) < 0)
44fa06
			return SLBT_BUFFER_ERROR(dctx);
44fa06
44fa06
		if ((slen = strlen(lnkarg)) + strlen(lnkname) + 1 >= PATH_MAX)
44fa06
			return SLBT_BUFFER_ERROR(dctx);
44fa06
44fa06
		mark    = &lnkarg[slen];
44fa06
		mark[0] = '/';
44fa06
		strcpy(++mark,lnkname);
44fa06
	}
e0a045
6529aa
	/* ln argv (fake) */
6529aa
	ln[0] = "ln";
6529aa
	ln[1] = "-s";
6529aa
	ln[2] = atarget;
e0a045
	ln[3] = lnkarg;
6529aa
	ln[4] = 0;
660491
660491
	oargv      = ectx->argv;
6529aa
	ectx->argv = ln;
6529aa
6529aa
	/* step output */
dc77cb
	if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT)) {
5e1c21
		if (dctx->cctx->mode == SLBT_MODE_LINK) {
33a569
			if (slbt_output_link(ectx)) {
660491
				ectx->argv = oargv;
d56ead
				return SLBT_NESTED_ERROR(dctx);
660491
			}
5e1c21
		} else {
33a569
			if (slbt_output_install(ectx)) {
660491
				ectx->argv = oargv;
d56ead
				return SLBT_NESTED_ERROR(dctx);
660491
			}
5e1c21
		}
dc77cb
	}
dc77cb
660491
	/* restore execution context */
660491
	ectx->argv = oargv;
660491
6529aa
	/* create symlink */
44fa06
	if (symlinkat(atarget,fddst,tmplnk))
6beda1
		return SLBT_SYSTEM_ERROR(dctx,tmplnk);
6529aa
44fa06
	return renameat(fddst,tmplnk,fddst,lnkname)
6beda1
		? SLBT_SYSTEM_ERROR(dctx,lnkname)
d56ead
		: 0;
6529aa
}
2baf1c
44fa06
slbt_hidden int slbt_create_symlink(
44fa06
	const struct slbt_driver_ctx *	dctx,
44fa06
	struct slbt_exec_ctx *		ectx,
44fa06
	const char *			target,
44fa06
	const char *			lnkname,
44fa06
	uint32_t			options)
44fa06
{
44fa06
	return slbt_create_symlink_ex(
44fa06
		dctx,ectx,
44fa06
		slbt_driver_fdcwd(dctx),
44fa06
		target,lnkname,options);
44fa06
}
44fa06
fc7ad9
slbt_hidden int slbt_symlink_is_a_placeholder(int fdcwd, const char * lnkpath)
2baf1c
{
2baf1c
	size_t		len;
2baf1c
	char		slink [PATH_MAX];
2baf1c
	char		target[PATH_MAX];
2baf1c
	const char	suffix[] = ".disabled";
2baf1c
2baf1c
	if ((sizeof(slink)-sizeof(suffix)) < (len=strlen(lnkpath)))
2baf1c
		return 0;
2baf1c
2baf1c
	memcpy(slink,lnkpath,len);
2baf1c
	memcpy(&slink[len],suffix,sizeof(suffix));
2baf1c
c81d16
	return (!slbt_readlinkat(fdcwd,slink,target,sizeof(target)))
2baf1c
		&& (!strcmp(target,"/dev/null"));
2baf1c
}