1 /* @(#)dirtime.c 1.34 18/08/31 Copyright 1988-2018 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)dirtime.c 1.34 18/08/31 Copyright 1988-2018 J. Schilling";
6 #endif
7 /*
8 * Copyright (c) 1988-2018 J. Schilling
9 */
10 /*
11 * The contents of this file are subject to the terms of the
12 * Common Development and Distribution License, Version 1.0 only
13 * (the "License"). You may not use this file except in compliance
14 * with the License.
15 *
16 * See the file CDDL.Schily.txt in this distribution for details.
17 * A copy of the CDDL is also available via the Internet at
18 * http://www.opensource.org/licenses/cddl1.txt
19 *
20 * When distributing Covered Code, include this CDDL HEADER in each
21 * file and include the License file CDDL.Schily.txt from this distribution.
22 */
23
24 /*
25 * Save directories and its times on a stack and set the times, if the new name
26 * will not increase the depth of the directory stack.
27 * The final flush of the stack is caused by a zero length filename.
28 *
29 * A string will be sufficient for the names of the directory stack because
30 * all directories in a tree have a common prefix. A counter for each
31 * occurence of a slash '/' is the index into the array of times for the
32 * directory stack. Directories with unknown times have atime.tv_nsec == -1.
33 *
34 * If the order of the files on tape is not in an order that find(1) will
35 * produce, this algorithm is not guaranteed to work. This is the case with
36 * tapes that have been created with the -r option or with the list= option.
37 *
38 * The only alternative would be saving all directory times and setting them
39 * at the end of an extract.
40 *
41 * NOTE: I am not shure if degenerate filenames will fool this algorithm.
42 */
43 #include <schily/types.h> /* includes <sys/types.h> needed for mode_t */
44 #include <schily/stdio.h>
45 #include <schily/standard.h>
46 #include <schily/string.h>
47 #define GT_COMERR /* #define comerr gtcomerr */
48 #define GT_ERROR /* #define error gterror */
49 #include <schily/schily.h>
50 #include "star.h"
51 #include "xutimes.h"
52 #include "checkerr.h"
53 #include "dirtime.h"
54 #include "starsubs.h"
55 #include "pathname.h"
56
57 #ifdef DEBUG
58 extern BOOL debug;
59 #define EDBG(a) if (debug) error a
60 #else
61 #define EDBG(a)
62 #endif
63
64 /*
65 * Maximum depth of directory nesting depends on availabe memory.
66 */
67 LOCAL pathstore_t dirstack;
68
69 #ifdef SET_CTIME
70 #define NT 3
71 LOCAL struct timespec dottimes[NT] = { {-1, -1}, {-1, -1}, {-1, -1}};
72 #else
73 #define NT 2
74 LOCAL struct timespec dottimes[NT] = { -1, -1, -1, -1};
75 #endif
76 LOCAL struct timespec badtime = { -1, -1};
77
78 typedef struct timespec timev[NT];
79
80 LOCAL pathstore_t dtps;
81 LOCAL int ndtimes;
82 LOCAL timev *dtimes;
83
84 LOCAL mode_t dotmodes = _BAD_MODE;
85
86 LOCAL pathstore_t dmps;
87 LOCAL int ndmodes;
88 LOCAL mode_t *dmodes;
89
90
91 LOCAL BOOL init_dirtimes __PR((void));
92 LOCAL BOOL grow_dtimes __PR((int n));
93 LOCAL BOOL grow_dmodes __PR((int n));
94 EXPORT void sdirtimes __PR((char *name, FINFO *info,
95 BOOL do_times, BOOL do_mode));
96 EXPORT void sdirmode __PR((char *name, mode_t mode));
97 LOCAL void dirtimes __PR((char *name, struct timespec *tp,
98 mode_t mode));
99 EXPORT void flushdirtimes __PR((void));
100 LOCAL void flushdirstack __PR((char *, char *, int));
101 LOCAL void setdirtime __PR((char *, struct timespec *));
102
103 LOCAL BOOL
init_dirtimes()104 init_dirtimes()
105 {
106 /*
107 * First the path string
108 */
109 if (init_pspace(PS_STDERR, &dirstack) < 0)
110 return (FALSE);
111 /*
112 * Now the time array
113 */
114 if (!grow_dtimes(1))
115 return (FALSE);
116
117 /*
118 * Now the mode array
119 */
120 if (!grow_dmodes(1))
121 return (FALSE);
122
123 return (TRUE);
124 }
125
126 LOCAL BOOL
grow_dtimes(n)127 grow_dtimes(n)
128 int n;
129 {
130 if ((n * sizeof (timev)) < ndtimes)
131 return (TRUE);
132
133 /*
134 * Add 1 since "ndtimes" is the size of the array while "n" is the
135 * next array index to use.
136 */
137 if (grow_pspace(PS_STDERR, &dtps, (n+1) * sizeof (timev)) < 0) {
138 return (FALSE);
139 }
140
141 dtimes = (timev *)dtps.ps_path;
142 ndtimes = dtps.ps_size / sizeof (timev);
143
144 return (TRUE);
145 }
146
147 LOCAL BOOL
grow_dmodes(n)148 grow_dmodes(n)
149 int n;
150 {
151 if ((n * sizeof (mode_t)) < ndmodes)
152 return (TRUE);
153
154 /*
155 * Add 1 since "ndmodes" is the size of the array while "n" is the
156 * next array index to use.
157 */
158 if (grow_pspace(PS_STDERR, &dmps, (n+1) * sizeof (mode_t)) < 0) {
159 return (FALSE);
160 }
161
162 dmodes = (mode_t *)dmps.ps_path;
163 ndmodes = dmps.ps_size / sizeof (mode_t);
164
165 return (TRUE);
166 }
167
168 /*
169 * This is the standard method to enter time and mode values
170 * into the directory stack.
171 */
172 EXPORT void
sdirtimes(name,info,do_times,do_mode)173 sdirtimes(name, info, do_times, do_mode)
174 char *name;
175 FINFO *info;
176 BOOL do_times;
177 BOOL do_mode;
178 {
179 struct timespec tp[NT];
180 mode_t mode = _BAD_MODE;
181
182 if (do_times) {
183 tp[0].tv_sec = info->f_atime;
184 tp[0].tv_nsec = info->f_ansec;
185
186 tp[1].tv_sec = info->f_mtime;
187 tp[1].tv_nsec = info->f_mnsec;
188 #ifdef SET_CTIME
189 tp[2].tv_sec = info->f_ctime;
190 tp[2].tv_nsec = info->f_cnsec;
191 #endif
192 } else {
193 tp[0] = badtime;
194 tp[1] = badtime;
195 #ifdef SET_CTIME
196 tp[2] = badtime;
197 #endif
198 }
199 if (do_mode) {
200 mode = info->f_mode;
201 }
202 dirtimes(name, tp, mode);
203 }
204
205 /*
206 * This is the method to enter mode values into the directory stack.
207 * It is only used to puch the umask value from _create_dirs().
208 */
209 EXPORT void
210 #ifdef PROTOTYPES
sdirmode(char * name,mode_t mode)211 sdirmode(char *name, mode_t mode)
212 #else
213 sdirmode(name, mode)
214 char *name;
215 mode_t mode;
216 #endif
217 {
218 struct timespec tp[NT];
219
220 tp[0] = badtime;
221 tp[1] = badtime;
222 #ifdef SET_CTIME
223 tp[2] = badtime;
224 #endif
225 dirtimes(name, tp, mode);
226 }
227
228 LOCAL void
229 #ifdef PROTOTYPES
dirtimes(char * name,struct timespec tp[NT],mode_t mode)230 dirtimes(char *name, struct timespec tp[NT], mode_t mode)
231 #else
232 dirtimes(name, tp, mode)
233 char *name;
234 struct timespec tp[NT];
235 mode_t mode;
236 #endif
237 {
238 register char *dp = dirstack.ps_path;
239 register char *np = name;
240 register int idx = -1;
241
242 if (dp == NULL) {
243 if (!init_dirtimes())
244 return;
245 dp = dirstack.ps_path;
246 }
247 EDBG(("dirtimes('%s', %s", name, tp ? ctime(&tp[1].tv_sec):"NULL\n"));
248
249 if (np[0] == '\0') { /* final flush */
250 if (dotmodes != _BAD_MODE) {
251 EDBG(("setmode: '.' to 0%o\n", dotmodes));
252 setdirmodes(".", dotmodes);
253 }
254 if (dottimes[0].tv_nsec != badtime.tv_nsec)
255 setdirtime(".", dottimes);
256 flushdirstack(dp, dp, -1);
257 return;
258 }
259
260 if ((np[0] == '.' && np[1] == '/' && np[2] == '\0') ||
261 (np[0] == '.' && np[1] == '\0')) {
262 dottimes[0] = tp[0];
263 dottimes[1] = tp[1];
264 #ifdef SET_CTIME
265 dottimes[2] = tp[2];
266 #endif
267 dotmodes = mode;
268 } else {
269 size_t nlen;
270
271 nlen = strlen(np);
272 if (nlen >= dirstack.ps_size) {
273 if (set_pspace(PS_STDERR, &dirstack, nlen+1) < 0) {
274 return;
275 }
276 dp = dirstack.ps_path;
277 }
278
279 /*
280 * Find end of common part
281 */
282 while (*dp == *np) {
283 if (*dp == '\0')
284 break;
285 if (*dp++ == '/')
286 ++idx;
287 np++;
288 }
289 /*
290 * Make sure that the ending '/' always stays in dirstack.
291 */
292 if (*dp == '/' && *np == '\0') {
293 dp++;
294 ++idx;
295 }
296 EDBG(("DIR: '%.*s' DP: '%s' NP: '%s' idx: %d\n",
297 /* XXX Should not be > int */
298 (int)(dp - dirstack.ps_path),
299 dirstack.ps_path, dp, np, idx));
300
301 if (*dp) {
302 /*
303 * New directory does not increase the depth of the
304 * directory stack. Flush all dirs below idx.
305 */
306 flushdirstack(dirstack.ps_path, dp, idx);
307 }
308
309 /*
310 * Put the new dir on the directory stack.
311 * First append the name component, then
312 * store times of "this" dir.
313 */
314 while ((*dp = *np++) != '\0') {
315 if (*dp++ == '/') {
316 /*
317 * Disable times of unknown dirs.
318 */
319 if (!grow_dtimes(++idx))
320 return;
321 if (!grow_dmodes(idx))
322 return;
323 EDBG(("zapping idx: %d\n", idx));
324 dtimes[idx][0] = badtime;
325 dmodes[idx] = _BAD_MODE;
326 } else if (*np == '\0') {
327 /*
328 * Make sure the dirname always ends with '/'.
329 */
330 *dp++ = '/';
331 *dp = '\0';
332 idx++;
333 }
334 }
335 if (tp) {
336 if (!grow_dtimes(idx))
337 return;
338 if (!grow_dmodes(idx))
339 return;
340 EDBG(("set idx %d '%s'\n", idx, name));
341 dtimes[idx][0] = tp[0]; /* overwrite last atime */
342 dtimes[idx][1] = tp[1]; /* overwrite last mtime */
343 #ifdef SET_CTIME
344 dtimes[idx][2] = tp[2]; /* overwrite last ctime */
345 #endif
346 dmodes[idx] = mode;
347 }
348 }
349 }
350
351 /*
352 * Needed for on_comerr() as we cannot pass the needed parameters to dirtimes().
353 */
354 EXPORT void
flushdirtimes()355 flushdirtimes()
356 {
357 dirtimes("", (struct timespec *)0, (mode_t)0);
358 }
359
360 LOCAL void
flushdirstack(dirbase,dp,depth)361 flushdirstack(dirbase, dp, depth)
362 char *dirbase;
363 register char *dp;
364 register int depth;
365 {
366 if (depth == -1 && dp == dirbase && dp[0] == '/') {
367 /*
368 * Flush the root dir, avoid flushing "".
369 */
370 while (*dp == '/')
371 dp++;
372 if (dmodes[++depth] != _BAD_MODE) {
373 EDBG(("depth: %d setmode: '/' to 0%o\n",
374 depth, dmodes[depth]));
375 setdirmodes("/", dmodes[depth]);
376 }
377 if (dtimes[depth][0].tv_nsec != badtime.tv_nsec) {
378 EDBG(("depth: %d ", depth));
379 setdirtime("/", dtimes[depth]);
380 }
381 }
382 /*
383 * The dirname always ends with a '/' (see above).
384 */
385 while (*dp) {
386 if (*dp++ == '/') {
387 *--dp = '\0'; /* temporarily delete '/' */
388 if (dmodes[++depth] != _BAD_MODE) {
389 EDBG(("depth: %d setmode: '%s' to 0%o\n",
390 depth, dirbase, dmodes[depth]));
391 setdirmodes(dirbase, dmodes[depth]);
392 }
393 if (dtimes[depth][0].tv_nsec != badtime.tv_nsec) {
394 EDBG(("depth: %d ", depth));
395 setdirtime(dirbase, dtimes[depth]);
396 }
397 *dp++ = '/'; /* restore '/' */
398 }
399 }
400 }
401
402 LOCAL void
setdirtime(name,tp)403 setdirtime(name, tp)
404 char *name;
405 struct timespec tp[NT];
406 {
407 EDBG(("settime: '%s' to %s", name, ctime(&tp[1].tv_sec)));
408 if (xutimes(name, tp, FALSE) < 0) {
409 if (!errhidden(E_SETTIME, name)) {
410 if (!errwarnonly(E_SETTIME, name))
411 xstats.s_settime++;
412 errmsg("Can't set time on '%s'.\n", name);
413 (void) errabort(E_SETTIME, name, TRUE);
414 }
415 }
416 }
417