1 /*******************************************************************/
2 /*  slibtool: a skinny libtool implementation, written in C        */
3 /*  Copyright (C) 2016--2018  Z. Gilboa                            */
4 /*  Released under the Standard MIT License; see COPYING.SLIBTOOL. */
5 /*******************************************************************/
6 
7 #include <stdio.h>
8 #include <string.h>
9 #include <stdbool.h>
10 #include <unistd.h>
11 
12 #include "slibtool_driver_impl.h"
13 #include "slibtool_errinfo_impl.h"
14 #include "slibtool_symlink_impl.h"
15 #include "slibtool_readlink_impl.h"
16 
17 #define SLBT_DEV_NULL_FLAGS	(SLBT_DRIVER_ALL_STATIC      \
18 				| SLBT_DRIVER_DISABLE_SHARED \
19 				| SLBT_DRIVER_DISABLE_STATIC)
20 
slbt_create_symlink(const struct slbt_driver_ctx * dctx,struct slbt_exec_ctx * ectx,const char * target,const char * lnkname,uint32_t options)21 int slbt_create_symlink(
22 	const struct slbt_driver_ctx *	dctx,
23 	struct slbt_exec_ctx *		ectx,
24 	const char *			target,
25 	const char *			lnkname,
26 	uint32_t			options)
27 {
28 	int		fdcwd;
29 	int		fliteral;
30 	int		fwrapper;
31 	int		fdevnull;
32 	char **		oargv;
33 	const char *	slash;
34 	char *		ln[5];
35 	char *		dotdot;
36 	char		tmplnk [PATH_MAX];
37 	char		lnkarg [PATH_MAX];
38 	char		alnkarg[PATH_MAX];
39 	char		atarget[PATH_MAX];
40 	char *		suffix = 0;
41 
42 	/* options */
43 	fliteral = (options & SLBT_SYMLINK_LITERAL);
44 	fwrapper = (options & SLBT_SYMLINK_WRAPPER);
45 	fdevnull = (options & SLBT_SYMLINK_DEVNULL);
46 
47 	/* symlink is a placeholder? */
48 	if (fliteral && fdevnull) {
49 		slash = target;
50 
51 	} else if ((dctx->cctx->drvflags & SLBT_DEV_NULL_FLAGS)
52 			&& !strcmp(target,"/dev/null")) {
53 		slash  = target;
54 		suffix = ".disabled";
55 
56 	/* target is an absolute path? */
57 	} else if (fliteral) {
58 		slash = target;
59 
60 	/* symlink target contains a dirname? */
61 	} else if ((slash = strrchr(target,'/'))) {
62 		slash++;
63 
64 	/* symlink target is a basename */
65 	} else {
66 		slash = target;
67 	}
68 
69 	/* .la wrapper? */
70 	dotdot = fwrapper ? "../" : "";
71 
72 	/* atarget */
73 	if ((size_t)snprintf(atarget,sizeof(atarget),"%s%s",
74 			dotdot,slash) >= sizeof(atarget))
75 		return SLBT_BUFFER_ERROR(dctx);
76 
77 	/* tmplnk */
78 	if ((size_t)snprintf(tmplnk,sizeof(tmplnk),"%s.symlink.tmp",
79 			lnkname) >= sizeof(tmplnk))
80 		return SLBT_BUFFER_ERROR(dctx);
81 
82 	/* placeholder? */
83 	if (suffix) {
84 		sprintf(alnkarg,"%s%s",lnkname,suffix);
85 		lnkname = alnkarg;
86 	}
87 
88 	/* lnkarg */
89 	strcpy(lnkarg,lnkname);
90 
91 	/* ln argv (fake) */
92 	ln[0] = "ln";
93 	ln[1] = "-s";
94 	ln[2] = atarget;
95 	ln[3] = lnkarg;
96 	ln[4] = 0;
97 
98 	oargv      = ectx->argv;
99 	ectx->argv = ln;
100 
101 	/* step output */
102 	if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT)) {
103 		if (dctx->cctx->mode == SLBT_MODE_LINK) {
104 			if (slbt_output_link(dctx,ectx)) {
105 				ectx->argv = oargv;
106 				return SLBT_NESTED_ERROR(dctx);
107 			}
108 		} else {
109 			if (slbt_output_install(dctx,ectx)) {
110 				ectx->argv = oargv;
111 				return SLBT_NESTED_ERROR(dctx);
112 			}
113 		}
114 	}
115 
116 	/* restore execution context */
117 	ectx->argv = oargv;
118 
119 	/* fdcwd */
120 	fdcwd = slbt_driver_fdcwd(dctx);
121 
122 	/* create symlink */
123 	if (symlinkat(atarget,fdcwd,tmplnk))
124 		return SLBT_SYSTEM_ERROR(dctx,tmplnk);
125 
126 	return renameat(fdcwd,tmplnk,fdcwd,lnkname)
127 		? SLBT_SYSTEM_ERROR(dctx,lnkname)
128 		: 0;
129 }
130 
slbt_symlink_is_a_placeholder(int fdcwd,char * lnkpath)131 int slbt_symlink_is_a_placeholder(int fdcwd, char * lnkpath)
132 {
133 	size_t		len;
134 	char		slink [PATH_MAX];
135 	char		target[PATH_MAX];
136 	const char	suffix[] = ".disabled";
137 
138 	if ((sizeof(slink)-sizeof(suffix)) < (len=strlen(lnkpath)))
139 		return 0;
140 
141 	memcpy(slink,lnkpath,len);
142 	memcpy(&slink[len],suffix,sizeof(suffix));
143 
144 	return (!slbt_readlinkat(fdcwd,slink,target,sizeof(target)))
145 		&& (!strcmp(target,"/dev/null"));
146 }
147