summaryrefslogtreecommitdiff
path: root/src/internal/slibtool_symlink_impl.c
blob: cc09a32f2559c33c53a49b69987c18829bb06fe3 (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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/*******************************************************************/
/*  slibtool: a skinny libtool implementation, written in C        */
/*  Copyright (C) 2016--2018  Z. Gilboa                            */
/*  Released under the Standard MIT License; see COPYING.SLIBTOOL. */
/*******************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>

#include "slibtool_errinfo_impl.h"
#include "slibtool_symlink_impl.h"
#include "slibtool_readlink_impl.h"

#define SLBT_DEV_NULL_FLAGS	(SLBT_DRIVER_ALL_STATIC      \
				| SLBT_DRIVER_DISABLE_SHARED \
				| SLBT_DRIVER_DISABLE_STATIC)

int slbt_create_symlink(
	const struct slbt_driver_ctx *	dctx,
	struct slbt_exec_ctx *		ectx,
	const char *			target,
	const char *			lnkname,
	bool				flawrapper)
{
	char **		oargv;
	const char *	slash;
	char *		ln[5];
	char *		dotdot;
	char		tmplnk [PATH_MAX];
	char		lnkarg [PATH_MAX];
	char		alnkarg[PATH_MAX];
	char		atarget[PATH_MAX];
	char *		suffix = 0;

	/* symlink is a placeholder? */
	if ((dctx->cctx->drvflags & SLBT_DEV_NULL_FLAGS)
			&& !strcmp(target,"/dev/null")) {
		slash  = target;
		suffix = ".disabled";

	/* symlink target contains a dirname? */
	} else if ((slash = strrchr(target,'/'))) {
		slash++;

	/* symlink target is a basename */
	} else {
		slash = target;
	}

	/* .la wrapper? */
	dotdot = flawrapper ? "../" : "";

	/* atarget */
	if ((size_t)snprintf(atarget,sizeof(atarget),"%s%s",
			dotdot,slash) >= sizeof(atarget))
		return SLBT_BUFFER_ERROR(dctx);

	/* tmplnk */
	if ((size_t)snprintf(tmplnk,sizeof(tmplnk),"%s.symlink.tmp",
			lnkname) >= sizeof(tmplnk))
		return SLBT_BUFFER_ERROR(dctx);

	/* placeholder? */
	if (suffix) {
		sprintf(alnkarg,"%s%s",lnkname,suffix);
		lnkname = alnkarg;
	}

	/* lnkarg */
	strcpy(lnkarg,lnkname);

	/* ln argv (fake) */
	ln[0] = "ln";
	ln[1] = "-s";
	ln[2] = atarget;
	ln[3] = lnkarg;
	ln[4] = 0;

	oargv      = ectx->argv;
	ectx->argv = ln;

	/* step output */
	if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT)) {
		if (dctx->cctx->mode == SLBT_MODE_LINK) {
			if (slbt_output_link(dctx,ectx)) {
				ectx->argv = oargv;
				return SLBT_NESTED_ERROR(dctx);
			}
		} else {
			if (slbt_output_install(dctx,ectx)) {
				ectx->argv = oargv;
				return SLBT_NESTED_ERROR(dctx);
			}
		}
	}

	/* restore execution context */
	ectx->argv = oargv;

	/* create symlink */
	if (symlink(atarget,tmplnk))
		return SLBT_SYSTEM_ERROR(dctx,tmplnk);

	return rename(tmplnk,lnkname)
		? SLBT_SYSTEM_ERROR(dctx,lnkname)
		: 0;
}

int slbt_symlink_is_a_placeholder(int fdcwd, char * lnkpath)
{
	size_t		len;
	char		slink [PATH_MAX];
	char		target[PATH_MAX];
	const char	suffix[] = ".disabled";

	if ((sizeof(slink)-sizeof(suffix)) < (len=strlen(lnkpath)))
		return 0;

	memcpy(slink,lnkpath,len);
	memcpy(&slink[len],suffix,sizeof(suffix));

	return (!slbt_readlinkat(fdcwd,slink,target,sizeof(target)))
		&& (!strcmp(target,"/dev/null"));
}