1 #include "system.h"
2 
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <sys/wait.h>
6 #include <errno.h>
7 #include <popt.h>
8 #include <ctype.h>
9 #include <pthread.h>
10 
11 #include <rpm/rpmfileutil.h>
12 #include <rpm/rpmurl.h>
13 #include <rpm/rpmmacro.h>
14 #include <rpm/rpmlog.h>
15 #include <rpm/argv.h>
16 
17 #include "rpmio/rpmio_internal.h"
18 
19 #include "debug.h"
20 
21 static const char *rpm_config_dir = NULL;
22 static pthread_once_t configDirSet = PTHREAD_ONCE_INIT;
23 
rpmDoDigest(int algo,const char * fn,int asAscii,unsigned char * digest)24 int rpmDoDigest(int algo, const char * fn,int asAscii, unsigned char * digest)
25 {
26     unsigned char * dig = NULL;
27     size_t diglen, buflen = 32 * BUFSIZ;
28     unsigned char *buf = xmalloc(buflen);
29     int rc = 0;
30 
31     FD_t fd = Fopen(fn, "r.ufdio");
32 
33     if (fd) {
34 	fdInitDigest(fd, algo, 0);
35 	while ((rc = Fread(buf, sizeof(*buf), buflen, fd)) > 0) {};
36 	fdFiniDigest(fd, algo, (void **)&dig, &diglen, asAscii);
37     }
38 
39     if (dig == NULL || Ferror(fd)) {
40 	rc = 1;
41     } else {
42 	memcpy(digest, dig, diglen);
43     }
44 
45     dig = _free(dig);
46     free(buf);
47     Fclose(fd);
48 
49     return rc;
50 }
51 
rpmMkTemp(char * templ)52 FD_t rpmMkTemp(char *templ)
53 {
54     mode_t mode;
55     int sfd;
56     FD_t tfd = NULL;
57 
58     mode = umask(0077);
59     sfd = mkstemp(templ);
60     umask(mode);
61 
62     if (sfd < 0) {
63 	goto exit;
64     }
65 
66     tfd = fdDup(sfd);
67     close(sfd);
68 
69 exit:
70     return tfd;
71 }
72 
rpmMkTempFile(const char * prefix,char ** fn)73 FD_t rpmMkTempFile(const char * prefix, char **fn)
74 {
75     const char *tpmacro = "%{_tmppath}"; /* always set from rpmrc */
76     char *tempfn;
77     static int _initialized = 0;
78     FD_t tfd = NULL;
79 
80     if (!prefix) prefix = "";
81 
82     /* Create the temp directory if it doesn't already exist. */
83     if (!_initialized) {
84 	_initialized = 1;
85 	tempfn = rpmGenPath(prefix, tpmacro, NULL);
86 	if (rpmioMkpath(tempfn, 0755, (uid_t) -1, (gid_t) -1))
87 	    goto exit;
88 	free(tempfn);
89     }
90 
91     tempfn = rpmGetPath(prefix, tpmacro, "/rpm-tmp.XXXXXX", NULL);
92     tfd = rpmMkTemp(tempfn);
93 
94     if (tfd == NULL || Ferror(tfd)) {
95 	rpmlog(RPMLOG_ERR, _("error creating temporary file %s: %m\n"), tempfn);
96 	goto exit;
97     }
98 
99 exit:
100     if (tfd != NULL && fn)
101 	*fn = tempfn;
102     else
103 	free(tempfn);
104 
105     return tfd;
106 }
107 
rpmioMkpath(const char * path,mode_t mode,uid_t uid,gid_t gid)108 int rpmioMkpath(const char * path, mode_t mode, uid_t uid, gid_t gid)
109 {
110     char *d, *de;
111     int rc;
112 
113     if (path == NULL || *path == '\0')
114 	return -1;
115     d = rstrcat(NULL, path);
116     if (d[strlen(d)-1] != '/') {
117 	rstrcat(&d,"/");
118     }
119     de = d;
120     for (;(de=strchr(de+1,'/'));) {
121 	struct stat st;
122 	*de = '\0';
123 	rc = stat(d, &st);
124 	if (rc) {
125 	    if (errno != ENOENT)
126 		goto exit;
127 	    rc = mkdir(d, mode);
128 	    if (rc)
129 		goto exit;
130 	    rpmlog(RPMLOG_DEBUG, "created directory(s) %s mode 0%o\n", path, mode);
131 	    if (!(uid == (uid_t) -1 && gid == (gid_t) -1)) {
132 		rc = chown(d, uid, gid);
133 		if (rc)
134 		    goto exit;
135 	    }
136 	} else if (!S_ISDIR(st.st_mode)) {
137 	    rc = ENOTDIR;
138 	    goto exit;
139 	}
140 	*de = '/';
141     }
142     rc = 0;
143 exit:
144     free(d);
145     return rc;
146 }
147 
rpmFileIsCompressed(const char * file,rpmCompressedMagic * compressed)148 int rpmFileIsCompressed(const char * file, rpmCompressedMagic * compressed)
149 {
150     FD_t fd;
151     ssize_t nb;
152     int rc = -1;
153     unsigned char magic[13];
154 
155     *compressed = COMPRESSED_NOT;
156 
157     fd = Fopen(file, "r.ufdio");
158     if (fd == NULL || Ferror(fd)) {
159 	/* XXX Fstrerror */
160 	rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
161 	if (fd) (void) Fclose(fd);
162 	return 1;
163     }
164     nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd);
165     if (nb < 0) {
166 	rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
167 	rc = 1;
168     } else if (nb < sizeof(magic)) {
169 	rpmlog(RPMLOG_ERR, _("File %s is smaller than %u bytes\n"),
170 		file, (unsigned)sizeof(magic));
171 	rc = 0;
172     }
173     (void) Fclose(fd);
174     if (rc >= 0)
175 	return rc;
176 
177     rc = 0;
178 
179     if ((magic[0] == 'B') && (magic[1] == 'Z') &&
180         (magic[2] == 'h')) {
181 	*compressed = COMPRESSED_BZIP2;
182     } else if ((magic[0] == 'P') && (magic[1] == 'K') &&
183 	 (((magic[2] == 3) && (magic[3] == 4)) ||
184 	  ((magic[2] == '0') && (magic[3] == '0')))) {	/* pkzip */
185 	*compressed = COMPRESSED_ZIP;
186     } else if ((magic[0] == 0xfd) && (magic[1] == 0x37) &&
187 	       (magic[2] == 0x7a) && (magic[3] == 0x58) &&
188 	       (magic[4] == 0x5a) && (magic[5] == 0x00)) {
189 	/* new style xz (lzma) with magic */
190 	*compressed = COMPRESSED_XZ;
191     } else if ((magic[0] == 0x28) && (magic[1] == 0xB5) &&
192 	       (magic[2] == 0x2f)                     ) {
193 	*compressed = COMPRESSED_ZSTD;
194     } else if ((magic[0] == 'L') && (magic[1] == 'Z') &&
195 	       (magic[2] == 'I') && (magic[3] == 'P')) {
196 	*compressed = COMPRESSED_LZIP;
197     } else if ((magic[0] == 'L') && (magic[1] == 'R') &&
198 	       (magic[2] == 'Z') && (magic[3] == 'I')) {
199 	*compressed = COMPRESSED_LRZIP;
200     } else if (((magic[0] == 0037) && (magic[1] == 0213)) || /* gzip */
201 	((magic[0] == 0037) && (magic[1] == 0236)) ||	/* old gzip */
202 	((magic[0] == 0037) && (magic[1] == 0036)) ||	/* pack */
203 	((magic[0] == 0037) && (magic[1] == 0240)) ||	/* SCO lzh */
204 	((magic[0] == 0037) && (magic[1] == 0235))	/* compress */
205 	) {
206 	*compressed = COMPRESSED_OTHER;
207     } else if ((magic[0] == '7') && (magic[1] == 'z') &&
208                (magic[2] == 0xbc) && (magic[3] == 0xaf) &&
209                (magic[4] == 0x27) && (magic[5] == 0x1c)) {
210 	*compressed = COMPRESSED_7ZIP;
211     } else if (rpmFileHasSuffix(file, ".lzma")) {
212 	*compressed = COMPRESSED_LZMA;
213     } else if (rpmFileHasSuffix(file, ".gem")) {
214 	*compressed = COMPRESSED_GEM;
215     }
216 
217     return rc;
218 }
219 
220 /* @todo "../sbin/./../bin/" not correct. */
rpmCleanPath(char * path)221 char *rpmCleanPath(char * path)
222 {
223     const char *s;
224     char *se, *t, *te;
225     int begin = 1;
226 
227     if (path == NULL)
228 	return NULL;
229 
230 /*fprintf(stderr, "*** RCP %s ->\n", path); */
231     s = t = te = path;
232     while (*s != '\0') {
233 /*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */
234 	switch (*s) {
235 	case ':':			/* handle url's */
236 	    if (s[1] == '/' && s[2] == '/') {
237 		*t++ = *s++;
238 		*t++ = *s++;
239 		break;
240 	    }
241 	    begin=1;
242 	    break;
243 	case '/':
244 	    /* Move parent dir forward */
245 	    for (se = te + 1; se < t && *se != '/'; se++)
246 		{};
247 	    if (se < t && *se == '/') {
248 		te = se;
249 /*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */
250 	    }
251 	    while (s[1] == '/')
252 		s++;
253 	    while (t > path && t[-1] == '/')
254 		t--;
255 	    break;
256 	case '.':
257 	    /* Leading .. is special */
258  	    /* Check that it is ../, so that we don't interpret */
259 	    /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */
260 	    /* in the case of "...", this ends up being processed*/
261 	    /* as "../.", and the last '.' is stripped.  This   */
262 	    /* would not be correct processing.                 */
263 	    if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
264 /*fprintf(stderr, "    leading \"..\"\n"); */
265 		*t++ = *s++;
266 		break;
267 	    }
268 	    /* Single . is special */
269 	    if (begin && s[1] == '\0') {
270 		break;
271 	    }
272 	    /* Handle the ./ cases */
273 	    if (t > path && t[-1] == '/') {
274 		/* Trim embedded ./ */
275 		if (s[1] == '/') {
276 		    s+=2;
277 		    continue;
278 		}
279 		/* Trim trailing /. */
280 		if (s[1] == '\0') {
281 		    s++;
282 		    continue;
283 		}
284 	    }
285 	    /* Trim embedded /../ and trailing /.. */
286 	    if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
287 		t = te;
288 		/* Move parent dir forward */
289 		if (te > path)
290 		    for (--te; te > path && *te != '/'; te--)
291 			{};
292 /*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */
293 		s++;
294 		s++;
295 		continue;
296 	    }
297 	    break;
298 	default:
299 	    begin = 0;
300 	    break;
301 	}
302 	*t++ = *s++;
303     }
304 
305     /* Trim trailing / (but leave single / alone) */
306     if (t > &path[1] && t[-1] == '/')
307 	t--;
308     *t = '\0';
309 
310 /*fprintf(stderr, "\t%s\n", path); */
311     return path;
312 }
313 
314 /* Merge 3 args into path, any or all of which may be a url. */
315 
rpmGenPath(const char * urlroot,const char * urlmdir,const char * urlfile)316 char * rpmGenPath(const char * urlroot, const char * urlmdir,
317 		const char *urlfile)
318 {
319     char * xroot = rpmGetPath(urlroot, NULL);
320     const char * root = xroot;
321     char * xmdir = rpmGetPath(urlmdir, NULL);
322     const char * mdir = xmdir;
323     char * xfile = rpmGetPath(urlfile, NULL);
324     const char * file = xfile;
325     char * result;
326     char * url = NULL;
327     int nurl = 0;
328     int ut;
329 
330     ut = urlPath(xroot, &root);
331     if (url == NULL && ut > URL_IS_DASH) {
332 	url = xroot;
333 	nurl = root - xroot;
334     }
335     if (root == NULL || *root == '\0') root = "/";
336 
337     ut = urlPath(xmdir, &mdir);
338     if (url == NULL && ut > URL_IS_DASH) {
339 	url = xmdir;
340 	nurl = mdir - xmdir;
341     }
342     if (mdir == NULL || *mdir == '\0') mdir = "/";
343 
344     ut = urlPath(xfile, &file);
345     if (url == NULL && ut > URL_IS_DASH) {
346 	url = xfile;
347 	nurl = file - xfile;
348     }
349 
350     if (url && nurl > 0) {
351 	char *t = rstrcat(NULL, url);
352 	t[nurl] = '\0';
353 	url = t;
354     } else
355 	url = xstrdup("");
356 
357     result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
358 
359     free(xroot);
360     free(xmdir);
361     free(xfile);
362     free(url);
363     return result;
364 }
365 
366 /* Return concatenated and expanded canonical path. */
367 
rpmGetPath(const char * path,...)368 char * rpmGetPath(const char *path, ...)
369 {
370     va_list ap;
371     char *dest = NULL, *res;
372     const char *s;
373 
374     if (path == NULL)
375 	return xstrdup("");
376 
377     va_start(ap, path);
378     for (s = path; s; s = va_arg(ap, const char *)) {
379 	rstrcat(&dest, s);
380     }
381     va_end(ap);
382 
383     res = rpmExpand(dest, NULL);
384     free(dest);
385 
386     return rpmCleanPath(res);
387 }
388 
rpmEscapeSpaces(const char * s)389 char * rpmEscapeSpaces(const char * s)
390 {
391     const char * se;
392     char * t;
393     char * te;
394     size_t nb = 0;
395 
396     for (se = s; *se; se++) {
397 	if (isspace(*se))
398 	    nb++;
399 	nb++;
400     }
401     nb++;
402 
403     t = te = xmalloc(nb);
404     for (se = s; *se; se++) {
405 	if (isspace(*se))
406 	    *te++ = '\\';
407 	*te++ = *se;
408     }
409     *te = '\0';
410     return t;
411 }
412 
rpmFileHasSuffix(const char * path,const char * suffix)413 int rpmFileHasSuffix(const char *path, const char *suffix)
414 {
415     size_t plen = strlen(path);
416     size_t slen = strlen(suffix);
417     return (plen >= slen && rstreq(path+plen-slen, suffix));
418 }
419 
rpmGetCwd(void)420 char * rpmGetCwd(void)
421 {
422     int currDirLen = 0;
423     char * currDir = NULL;
424 
425     do {
426 	currDirLen += 128;
427 	currDir = xrealloc(currDir, currDirLen);
428 	memset(currDir, 0, currDirLen);
429     } while (getcwd(currDir, currDirLen) == NULL && errno == ERANGE);
430 
431     return currDir;
432 }
433 
rpmMkdirs(const char * root,const char * pathstr)434 int rpmMkdirs(const char *root, const char *pathstr)
435 {
436     ARGV_t dirs = NULL;
437     int rc = 0;
438     argvSplit(&dirs, pathstr, ":");
439 
440     for (char **d = dirs; *d; d++) {
441 	char *path = rpmGetPath(root ? root : "", *d, NULL);
442 	if (strstr(path, "%{"))
443 	    rpmlog(RPMLOG_WARNING, ("undefined macro(s) in %s: %s\n"), *d, path);
444 	if ((rc = rpmioMkpath(path, 0755, -1, -1)) != 0) {
445 	    const char *msg = _("failed to create directory");
446 	    /* try to be more informative if the failing part was a macro */
447 	    if (**d == '%') {
448 	    	rpmlog(RPMLOG_ERR, "%s %s: %s: %m\n", msg, *d, path);
449 	    } else {
450 	    	rpmlog(RPMLOG_ERR, "%s %s: %m\n", msg, path);
451 	    }
452 	}
453 	free(path);
454 	if (rc) break;
455     }
456     argvFree(dirs);
457     return rc;
458 }
459 
setConfigDir(void)460 static void setConfigDir(void)
461 {
462     char *rpmenv = getenv("RPM_CONFIGDIR");
463     rpm_config_dir = rpmenv ? xstrdup(rpmenv) : RPMCONFIGDIR;
464 }
465 
rpmConfigDir(void)466 const char *rpmConfigDir(void)
467 {
468     pthread_once(&configDirSet, setConfigDir);
469     return rpm_config_dir;
470 }
471