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