time-t-casting-fixes-20060404
[openafs.git] / src / package / update.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*------------------------------------------------------------------------
11  * update.c
12  *
13  * Description:
14  *      Routines that actually do the disk updates.
15  *
16  *------------------------------------------------------------------------*/
17
18 #include <afs/param.h>
19 #include <stdio.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/param.h>
23 #include <dirent.h>
24 #include <time.h>
25 #include <sys/time.h>
26 #if defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV)
27 #include <sys/mkdev.h>
28 #endif
29 #ifdef AFS_LINUX20_ENV
30 #include <sys/sysmacros.h>
31 #endif
32 #include "globals.h"
33 #include "package.h"
34
35 char *emalloc();
36 char *strcpy();
37 CTREEPTR LocateChildNode();
38
39 static struct stat stb;
40
41 static UpdateSock();
42 static UpdateDev();
43 static UpdatePipe();
44 static UpdateLnk();
45 static UpdateDir();
46 static UpdateReg();
47 static dochtyp();
48 static dochmod();
49 static dochown();
50 static dochtim();
51 static int FixLostFoundDir();
52 static int FixDir();
53 static FixReg();
54
55 /* $$important: these will have to be fixed with an error recovery mechanism */
56
57 int
58 update(CTREEPTR np, char *path)
59 {                               /*update */
60
61     switch (np->type) {
62 #ifndef AFS_AIX_ENV
63     case S_IFSOCK:
64         UpdateSock(np, path);
65         break;
66 #endif /* AFS_AIX_ENV */
67
68 #ifdef S_IFIFO
69     case S_IFIFO:
70         UpdatePipe(np, path);
71         break;
72 #endif /* S_IFIFO */
73
74     case S_IFCHR:
75     case S_IFBLK:
76         UpdateDev(np, path);
77         break;
78
79     case S_IFLNK:
80         UpdateLnk(np, path);
81         break;
82
83     case S_IFDIR:
84         UpdateDir(np, path);
85         break;
86
87     case S_IFREG:
88         UpdateReg(np, path);
89         break;
90     }
91
92 }                               /*update */
93
94 static void
95 UpdateSock(CTREEPTR np, char *path)
96 {                               /*UpdateSock */
97
98     (void)dochtyp(np, path);
99
100 }                               /*UpdateSock */
101
102
103 static void
104 UpdateDev(CTREEPTR np, char *path)
105 {                               /*UpdateDev */
106
107     register int ret;
108
109     ret = dochtyp(np, path);
110
111 #ifdef  KFLAG
112     if (ret == 1)
113         return;
114 #endif /* KFLAG */
115     if ((np->flag & F_PROTO) != 0) {
116         if (ret >= 0) {
117             if (np->proto.info.rdev != stb.st_rdev) {
118                 rm(path);
119                 ret = -1;
120             }
121         }
122         if (ret < 0) {
123             char *type;
124
125             switch (np->type) {
126             case S_IFBLK:
127                 type = "b";
128                 break;
129
130             case S_IFCHR:
131                 type = "c";
132                 break;
133
134             default:
135                 message("Unknown device type: %d\n", np->type);
136                 break;
137             }
138
139             loudonly_message("mknod %s %d %d %s", type,
140                              major(np->proto.info.rdev),
141                              minor(np->proto.info.rdev), path);
142             if (!opt_lazy) {
143                 if (mknod
144                     (path, (int)np->mode | (int)np->type,
145                      (int)np->proto.info.rdev) < 0)
146                     message("mknod %s %d %d %s; %m", type,
147                             major(np->proto.info.rdev),
148                             minor(np->proto.info.rdev), path);
149                 if ((ret = lstat(path, &stb)) < 0)
150                     message("lstat %s; %m", path);
151             }
152         }
153     }
154     if (ret >= 0) {
155         dochmod(np, path);
156         dochown(np, path);
157     }
158
159 }                               /*UpdateDev */
160
161 static void
162 UpdatePipe(CTREEPTR np, char *path)
163 {                               /*UpdatePipe */
164
165     register int ret;
166
167     /*
168      * Don't have to call dochtyp() here; just set ret to the value
169      * saying everything is fine.
170      */
171     ret = -1;
172
173 #ifdef  KFLAG
174     if (ret == 1)
175         return;
176 #endif /* KFLAG */
177     if ((np->flag & F_PROTO) != 0) {
178         if (ret >= 0) {
179             if (np->proto.info.rdev != stb.st_rdev) {
180                 rm(path);
181                 ret = -1;
182             }
183         }
184         if (ret < 0) {
185             loudonly_message("mknod p %s", path);
186
187             if (!opt_lazy) {
188                 if (mknod
189                     (path, (int)(np->mode) | (int)(np->type),
190                      (int)(np->proto.info.rdev)) < 0)
191                     message("mknod p %s; %m", path);
192                 if ((ret = lstat(path, &stb)) < 0)
193                     message("lstat %s; %m", path);
194             }
195         }
196     }
197
198     if (ret >= 0) {
199         dochmod(np, path);
200         dochown(np, path);
201     }
202
203 }                               /*UpdatePipe */
204
205 static void
206 UpdateLnk(CTREEPTR np, char *path)
207 {                               /*UpdateLnk */
208
209     register int ret;
210     char temp[MAXPATHLEN], temp2[MAXPATHLEN];
211     int cc;
212
213     ret = dochtyp(np, path);
214 #ifdef  KFLAG
215     if (ret == 1)
216         return;
217 #endif /* KFLAG */
218     if ((np->flag & F_PROTO) == 0)
219         return;
220     if (np->updtspec & U_ABSPATH)
221         sprintf(temp, "%s", np->proto.info.path);
222     else
223         sprintf(temp, "%s%s", np->proto.info.path, path);
224     if (ret >= 0) {
225         if ((cc = readlink(path, temp2, sizeof(temp2) - 1)) < 0) {
226             message("readlink %s; %m", path);
227             return;
228         }
229         temp2[cc] = 0;
230         if (strcmp(temp2, temp)) {
231             if ((np->updtspec & U_NOOVERWRITE) == 0) {
232                 rm(path);
233                 ret = -1;
234             } else {
235                 loudonly_message("INHIBIT %s updating", path);
236             }
237         }
238     }
239     if (ret < 0) {
240         loudonly_message("ln %s %s", path, temp);
241         if (!opt_lazy && symlink(temp, path) < 0)
242             message("symlink %s %s; %m", temp, path);
243     }
244
245 }                               /*UpdateLnk */
246
247
248 static void
249 UpdateDir(CTREEPTR np, char *path)
250 {                               /*UpdateDir */
251
252     register int ret;
253
254     ret = dochtyp(np, path);
255 #ifdef  KFLAG
256     if (ret == 1)
257         return;
258 #endif /* KFLAG */
259     if (ret < 0) {
260         loudonly_message("mkdir %s", path);
261         if (!opt_lazy) {
262             if (mkdir(path, (int)np->mode & ~S_IFMT) < 0)
263                 message("mkdir %s; %m", path);
264             if ((ret = lstat(path, &stb)) < 0)
265                 message("lstat %s; %m", path);
266         }
267     }
268     if (np->updtspec & U_LOSTFOUND)
269         (void)FixLostFoundDir(path);
270     if (np->updtspec & U_RMEXTRA)
271         (void)FixDir(np, path);
272     if (ret >= 0) {
273         dochmod(np, path);
274         dochown(np, path);
275     }
276
277 }                               /*UpdateDir */
278
279
280 static void
281 UpdateReg(CTREEPTR np, char *path)
282 {                               /*UpdateReg */
283
284     register int ret;
285
286     ret = dochtyp(np, path);
287 #ifdef  KFLAG
288     if (ret == 1)
289         return;
290 #endif /* KFLAG */
291     if ((np->flag & F_PROTO) != 0) {
292         if (ret < 0)
293             np->updtspec &= ~U_RENAMEOLD;
294         if (ret >= 0) {
295             if ((np->updtspec & U_NOOVERWRITE) == 0)
296                 if (np->mtime != stb.st_mtime)
297                     ret = -1;
298         }
299         if (ret < 0) {
300             if ((ret = FixReg(np, path)) >= 0)
301                 ret = lstat(path, &stb);
302             if (ret >= 0)
303                 dochtim(np, path);
304         }
305     }
306     if (ret >= 0) {
307         dochmod(np, path);
308         dochown(np, path);
309     }
310
311 }                               /*UpdateReg */
312
313
314 /*
315  * dochtyp
316  *
317  * This function makes sure the path on local disk has the same file type
318  * as that in the given prototype.  If it doesn't (and the -rebootfile
319  * flag hasn't been used with a file marked as requiring a reboot), then
320  * we delete the local disk copy and return -1.  If inhibiting the overwrite
321  * is in order, we return 1.  If the types already match (or the above
322  * reboot scenario is true), we return 0.
323  */
324
325 static int
326 dochtyp(CTREEPTR np, char *path)
327 {                               /*dochtyp */
328     if (lstat(path, &stb) < 0)
329         return -1;
330 #ifdef  KFLAG
331     if (opt_kflag && (stb.st_mode & 0222) == 0) {
332         loudonly_message("INHIBIT %s updating", path);
333         return 1;
334     }
335 #endif /* KFLAG */
336     if ((stb.st_mode & S_IFMT) == np->type)
337         return 0;
338     if (!opt_reboot && (np->flag & F_UPDT) && (np->updtspec & U_REBOOT)) {
339         message("%s is out of date; please REBOOT!", path);
340         return 0;
341     } else {
342         rm(path);
343         return -1;
344     }
345 }                               /*dochtyp */
346
347 static void
348 dochmod(CTREEPTR np, char *path)
349 {                               /*dochmod */
350     if ((np->flag & F_MODE) == 0)
351         return;
352     if ((np->mode & ~S_IFMT) == (stb.st_mode & ~S_IFMT))
353         return;
354     loudonly_message("chmod %s %o", path, np->mode & ~S_IFMT);
355     if (!opt_lazy && chmod(path, (int)np->mode & ~S_IFMT) < 0)
356         message("chmod %s; %m", path);
357 }                               /*dochmod */
358
359 static void
360 dochown(CTREEPTR np, char *path)
361 {                               /*dochown */
362     if ((np->flag & F_UID) == 0)
363         np->uid = stb.st_uid;
364     if ((np->flag & F_GID) == 0)
365         np->gid = stb.st_gid;
366     if (np->uid == stb.st_uid && np->gid == stb.st_gid)
367         return;
368     loudonly_message("chown %s %d %d", path, np->uid, np->gid);
369     if (!opt_lazy && chown(path, np->uid, np->gid) < 0)
370         message("chown %s; %m", path);
371 }                               /*dochown */
372
373 static void
374 dochtim(CTREEPTR np, char *path)
375 {                               /*dochtim */
376     struct timeval tm[2];
377
378     if (np->mtime == stb.st_mtime
379         || (!opt_reboot && (np->updtspec & U_REBOOT)))
380         return;
381     tm[0].tv_sec = tm[1].tv_sec = np->mtime;
382     tm[0].tv_usec = tm[1].tv_usec = 0;
383     if (!opt_silent) {
384         char *date;
385         time_t mtime = np->mtime;
386
387         date = ctime(&mtime);
388         date[24] = 0;
389         loudonly_message("utimes %s [%s]", path, date);
390     }
391     if (!opt_lazy && utimes(path, tm) < 0)
392         message("utimes %s; %m", path);
393 }                               /*dochtim */
394
395 static int
396 FixLostFoundDir(char *path)
397 {                               /*FixLostFoundDir */
398     if (stb.st_size >= 3584)
399         return 0;
400     return mklostfound(path);
401 }                               /*FixLostFoundDir */
402
403 static int
404 FixDir(CTREEPTR np, char *path)
405 {                               /*FixDir */
406     register DIR *dp;
407     register struct dirent *de;
408     register char *endp;
409
410     verbose_message("cleandir %s", path);
411     if ((dp = opendir(path)) == 0) {
412         message("opendir %s; %m", path);
413         return -1;
414     }
415     endp = path + strlen(path);
416     *endp++ = '/';
417     while ((de = readdir(dp)) != 0) {
418         if (de->d_name[0] == '.') {
419             if (de->d_name[1] == 0)
420                 continue;
421             if (de->d_name[1] == '.' && de->d_name[2] == 0)
422                 continue;
423         }
424         if (LocateChildNode(np, de->d_name, C_LOCATE) != 0)
425             continue;
426         (void)strcpy(endp, de->d_name);
427         rm(path);
428     }
429     *--endp = 0;
430     (void)closedir(dp);
431     return 0;
432 }                               /*FixDir */
433
434 static int
435 FixReg(CTREEPTR np, char *path)
436 {                               /*FixReg */
437     char new[MAXPATHLEN], old[MAXPATHLEN], temp[MAXPATHLEN];
438
439     if (!opt_reboot && (np->updtspec & U_REBOOT)) {
440         verbose_message
441             ("%s is a 'Q' file and -rebootfiles is set; not updated!", path);
442         return 0;
443     }
444     (void)sprintf(new, "%s.new", path);
445     if (np->updtspec & U_ABSPATH)
446         (void)sprintf(temp, "%s", np->proto.info.path);
447     else
448         (void)sprintf(temp, "%s%s", np->proto.info.path, path);
449     if (cp(temp, new))
450         return -1;
451     if (np->updtspec & U_RENAMEOLD) {
452         (void)sprintf(old, "%s.old", path);
453         (void)rm(old);
454         (void)ln(path, old);
455     }
456     if (mv(new, path))
457         return -1;
458     if (np->updtspec & U_REBOOT)
459         status = status_reboot;
460     return 0;
461 }                               /*FixReg */