1 /* @(#)get.c	1.4 21/07/26 Copyright 2011-2021 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)get.c	1.4 21/07/26 Copyright 2011-2021 J. Schilling";
6 #endif
7 /*
8  *	Get a file from a StreamArchive and store it in the filesystem
9  *
10  *	Copyright (c) 2011-2021 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 #include <schily/stdio.h>
27 #include <schily/stdlib.h>
28 #include <schily/utypes.h>
29 #include <schily/fcntl.h>
30 #include <schily/device.h>
31 #include <schily/errno.h>
32 #include <schily/string.h>
33 #include <schily/schily.h>
34 #include <schily/maxpath.h>
35 #include <schily/strar.h>
36 #include "table.h"
37 
38 #define	my_uid		strar_my_uid
39 #define	mode_mask	strar_mode_mask
40 
41 #define	ROOT_UID	0
42 
43 #if defined(ENOTEMPTY) && ENOTEMPTY != EEXIST
44 #define	is_eexist(err)	((err) == EEXIST || (err) == ENOTEMPTY)
45 #else
46 #define	is_eexist(err)	((err) == EEXIST)
47 #endif
48 #if defined(EMISSDIR)
49 #define	is_enoent(err)	((err) == ENOENT || (err) == EMISSDIR)
50 #else
51 #define	is_enoent(err)	((err) == ENOENT)
52 #endif
53 
54 #define	remove_file(n, b)	(0)
55 
56 extern	uid_t	my_uid;
57 extern	mode_t	mode_mask;
58 
59 LOCAL	int	strar_getfile	__PR((FINFO *info, const char *name));
60 LOCAL	BOOL	strar_tmpname	__PR((FINFO *info, char	*xname));
61 LOCAL	BOOL	_create_dirs	__PR((char *name));
62 LOCAL	BOOL	create_dirs	__PR((char *name));
63 
64 int
strar_get(info)65 strar_get(info)
66 	register FINFO	*info;
67 {
68 	char	nbuf[4096];
69 	int	n;
70 
71 	if (info->f_name[0] == '.' && info->f_name[1] == '\0') {
72 		nbuf[0] = '\0';
73 	} else {
74 		strar_tmpname(info, nbuf);
75 		create_dirs(nbuf);
76 	}
77 
78 	switch (info->f_xftype) {
79 
80 	case XT_FILE:
81 	case XT_CONT:
82 			if (strar_getfile(info, nbuf) < 0) {
83 				goto err;
84 			}
85 			break;
86 
87 	case XT_LINK:
88 			if (link(info->f_lname, nbuf) < 0) {
89 				errmsg("Cannot make link '%s'\n", nbuf);
90 				goto err;
91 			}
92 			break;
93 
94 	case XT_SLINK:
95 			if (link(info->f_lname, nbuf) < 0) {
96 				errmsg("Cannot make symlink '%s'\n", nbuf);
97 				goto err;
98 			}
99 			break;
100 
101 	case XT_DIR:
102 			if (nbuf[0] &&
103 			    mkdir(nbuf, info->f_mode | S_IWUSR) < 0) {
104 				errmsg("Cannot make directory '%s'\n", nbuf);
105 				goto err;
106 			}
107 			break;
108 	case XT_FIFO:
109 			if (mkfifo(nbuf, info->f_mode) < 0) {
110 				errmsg("Cannot make fifo '%s'\n", nbuf);
111 				goto err;
112 			}
113 			break;
114 
115 	default:
116 			if (mknod(nbuf, info->f_mode,
117 			    makedev(info->f_rdevmaj, info->f_rdevmin)) < 0) {
118 				errmsg("Cannot make special '%s'\n", nbuf);
119 				goto err;
120 			}
121 	}
122 	/*
123 	 * Read status
124 	 */
125 	n = strar_hparse(info);
126 	if (!n || info->f_status != 0) {
127 		if (info->f_xftype != XT_DIR)
128 			unlink(nbuf);
129 		else
130 			rmdir(nbuf);
131 		goto err;
132 	}
133 	if (nbuf[0] == '\0')
134 		return (0);
135 	if (rename(nbuf, info->f_name) >= 0)
136 		return (0);
137 	errmsgno(EX_BAD, "Could not extract '%s'.\n", info->f_name);
138 err:
139 	return (-1);
140 }
141 
142 LOCAL int
strar_getfile(info,name)143 strar_getfile(info, name)
144 	register FINFO	*info;
145 	const	char	*name;
146 {
147 	char	buf[32*1024];
148 	off_t	n;
149 	off_t	amt;
150 	FILE	*f = info->f_fp;
151 	int	fd;
152 
153 	fd = creat(name, info->f_mode | S_IWUSR);
154 	if (fd < 0) {
155 		errmsg("Cannot create '%s'.\n", name);
156 		strar_skip(info);
157 		return (-1);
158 	}
159 	for (n = 0; n < info->f_size; n += amt) {
160 		amt = info->f_size - n;
161 		if (amt > sizeof (buf))
162 			amt = sizeof (buf);
163 
164 		amt = fileread(f, buf, amt);
165 		if (amt < 0) {
166 			errmsg("Cannot read '%s'.\n", info->f_fpname);
167 			close(fd);
168 			return (-1);
169 		}
170 		if (write(fd, buf, amt) != amt) {
171 			errmsg("Cannot write '%s'.\n", name);
172 			close(fd);
173 			n += amt;
174 			goto err;
175 		}
176 	}
177 	close(fd);
178 	return (0);
179 err:
180 	for (; n < info->f_size; n++)
181 		getc(f);
182 	return (-1);
183 }
184 
185 /*
186  * Create a temporary path name for the extraction in -install mode.
187  */
188 LOCAL BOOL
strar_tmpname(info,xname)189 strar_tmpname(info, xname)
190 	FINFO	*info;
191 	char	*xname;
192 {
193 	register char	*xp = xname;
194 	register char	*np;
195 	register char	*dp;
196 
197 	np = info->f_name;
198 	dp = xp;
199 	do {
200 		if ((*xp++ = *np) == '/')
201 			dp = xp;
202 	} while (*np++);
203 	if ((dp - xname) >= (PATH_MAX-6)) {
204 		errmsgno(ENAMETOOLONG,
205 				"Cannot make temporary name for '%s'.\n",
206 				info->f_name);
207 		return (FALSE);
208 	}
209 	strcpy(dp, "XXXXXX");
210 	seterrno(0);
211 	mktemp(xname);
212 	if (xname[0] == '\0') {
213 		errmsg("Cannot make temporary name for '%s'.\n",
214 				info->f_name);
215 		return (FALSE);
216 	}
217 	return (TRUE);
218 }
219 
220 /*
221  * Create intermediate directories.
222  * If the user is not root and the umask is degenerated or read-only,
223  * we add 0700 to the granted permissions. For this reason, we may need
224  * to correct the permissins of intermediate directories later from the
225  * directory stack.
226  */
227 LOCAL BOOL
_create_dirs(name)228 _create_dirs(name)
229 	register char	*name;
230 {
231 	mode_t	mode;
232 
233 	mode = mode_mask;				/* used to be 0777 */
234 	if (my_uid != ROOT_UID)
235 		mode |= S_IRWXU;	/* Make sure we will be able write */
236 
237 	if (mkdir(name, mode) < 0) {
238 		if (create_dirs(name) &&
239 		    mkdir(name, mode) >= 0) {
240 #ifdef	__needed__
241 			_dir_setownwer(name);
242 			if (mode != mode_mask)
243 				sdirmode(name, mode_mask); /* Push umask */
244 #endif
245 			return (TRUE);
246 		}
247 		return (FALSE);
248 	}
249 #ifdef	__needed__
250 	_dir_setownwer(name);
251 	if (mode != mode_mask)
252 		sdirmode(name, mode_mask);	/* Push umask on dirstack */
253 #endif
254 	return (TRUE);
255 }
256 
257 #ifdef	__needed__
258 /*
259  * Set the owner/group of intermedia directories.
260  * Be very careful not to overwrite sgid directory semantics.
261  */
262 LOCAL void
_dir_setownwer(name)263 _dir_setownwer(name)
264 	char	*name;
265 {
266 	FINFO	dinfo;
267 
268 	if (my_uid != ROOT_UID)
269 		return;
270 
271 	if (dir_uid == _BAD_UID && dir_gid == _BAD_GID)
272 		return;
273 
274 	if (!_getinfo(name, &dinfo) || !is_dir(&dinfo))
275 		return;
276 
277 	if (dir_uid != _BAD_UID)
278 		dinfo.f_uid = dir_uid;
279 	if (dir_gid != _BAD_GID)
280 		dinfo.f_gid = dir_gid;
281 
282 	chown(name, dinfo.f_uid, dinfo.f_gid);
283 }
284 #endif
285 
286 EXPORT BOOL
create_dirs(name)287 create_dirs(name)
288 	register char	*name;
289 {
290 	register char	*np;
291 	register char	*dp;
292 		int	err;
293 		int	olderr = 0;
294 
295 #ifdef	__needed__
296 	if (noxdir) {
297 		errmsgno(EX_BAD, "Directories not created.\n");
298 		return (FALSE);
299 	}
300 #endif
301 	np = dp = name;
302 	do {
303 		if (*np == '/')
304 			dp = np;
305 	} while (*np++);
306 	if (dp == name) {
307 		/*
308 		 * Do not create the very last directory
309 		 */
310 		return (TRUE);
311 	}
312 	*dp = '\0';
313 	if (access(name, F_OK) < 0) {
314 		if (_create_dirs(name)) {
315 			*dp = '/';
316 			return (TRUE);
317 		}
318 		err = geterrno();
319 		if ((err == EACCES || is_eexist(err))) {
320 			olderr = err;
321 			goto exists;
322 		}
323 		*dp = '/';
324 		return (FALSE);
325 	} else {
326 		struct stat dinfo;
327 
328 	exists:
329 		if (lstat(name, &dinfo)) {
330 			if (S_ISDIR(dinfo.st_mode)) {
331 				*dp = '/';
332 				return (TRUE);
333 			}
334 
335 			if (remove_file(name, FALSE)) {
336 				if (_create_dirs(name)) {
337 					*dp = '/';
338 					return (TRUE);
339 				}
340 				*dp = '/';
341 				return (FALSE);
342 			} else {
343 				*dp = '/';
344 				return (FALSE);
345 			}
346 		}
347 		*dp = '/';
348 		if (olderr == EACCES)
349 			seterrno(olderr);
350 		return (FALSE);
351 	}
352 }
353