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