97434dc86bb47373b591a9b769052b20815d15f3
[openafs.git] / src / package / check.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  * check.c
12  *
13  * Description:
14  *      Check the integrity of the configuration tree for package, the
15  *      AFS workstation configuration tool.
16  *
17  *------------------------------------------------------------------------*/
18
19 #include <afs/param.h>
20 #include <stdio.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <sys/param.h>
24 #include <dirent.h>
25 #include "globals.h"
26 #include "package.h"
27
28 char *emalloc();
29 char *strcpy();
30 CTREEPTR LocateChildNode();
31
32 static char path2[MAXPATHLEN + 1];      /* $$get rid of this */
33 static char path3[MAXPATHLEN + 1];      /* $$get rid of this */
34
35 /*------------------------------------------------------------------------
36  * [static] CheckMount
37  *
38  * Description:
39  *      Check the assertion that the given path is a Unix mountpoint.
40  *
41  * Arguments:
42  *      char *path : Path to check.
43  *
44  * Returns:
45  *      0 on success,
46  *      Exit from package on failure.
47  *
48  * Environment:
49  *      This routine is private to the module.
50  *
51  * Side Effects:
52  *      May exit from package.
53  *------------------------------------------------------------------------*/
54
55 static void
56 CheckMount(char *path)
57 {                               /*CheckMount */
58     struct stat stb;            /*Parent's stat block */
59     struct stat stb2;           /*Child's stat block */
60     char dir[MAXPATHLEN];       /*Pathname of candidate mount point */
61     char parent[MAXPATHLEN];    /*Parent's pathname */
62     register char *ep, *dp, *sp;        /*Sliding ptr to above strings */
63     int ret;                    /*Return value */
64
65     debug_message("%% CheckMount called on path %s", path);
66     ret = 0;
67
68     /*
69      * Copy out the candidate mountpoint's pathname into dir, throwing
70      * off any leaf component from the original path.
71      */
72     ep = strrchr(path, '/');
73     for (sp = path, dp = dir; sp < ep; *dp++ = *sp++);
74     if (dp == dir)
75         *dp++ = '/';
76     *dp = '\0';
77
78     /*
79      * Copy out the parent's pathname into parent.
80      */
81     ep = strrchr(dir, '/');
82     for (sp = dir, dp = parent; sp < ep; *dp++ = *sp++);
83     if (dp == parent)
84         *dp++ = '/';
85     *dp = '\0';
86
87     /*
88      * Only perform the following test if the candidate mountpoint is
89      * something other than `/', which we know is a mountpoint.
90      */
91     if (strcmp(dir, "/")) {
92         /*
93          * Stat the given directory and its parent.  If either is not a
94          * directory or if the device numbers are the same, then the
95          * candidate has failed and is not a Unix mountpoint.
96          */
97         if (stat(dir, &stb) < 0)
98             ret = -1;
99         if (stat(parent, &stb2) < 0)
100             ret = -1;
101         if ((stb.st_mode & S_IFMT) != S_IFDIR)
102             ret = -1;
103         if ((stb2.st_mode & S_IFMT) != S_IFDIR)
104             ret = -1;
105         if (stb2.st_dev == stb.st_dev)
106             ret = -1;
107     }
108
109     if (ret < 0) {
110         /*
111          * Our assertion that the given path is a mountpoint is false.
112          * Tell our caller, then croak off.
113          */
114         fatal("** %s is not a Unix mountpoint, as was expected!", dir);
115     }
116
117     /*
118      * The candidate mountpoint has passed the test.  If we're being
119      * verbose, tell everyone.
120      */
121     verbose_message("Found Unix mountpoint %s", dir);
122 }                               /*CheckMount */
123
124 /*------------------------------------------------------------------------
125  * check
126  *
127  * Description:
128  *      Check the validity of the given node compared to the associated
129  *      pathname.
130  *
131  * Arguments:
132  *      CTREEPTR np : Node pointer to check.
133  *      char *path  : Associated pathname.
134  *
135  * Returns:
136  *      0 upon success,
137  *      Exits the program otherwise.
138  *
139  * Environment:
140  *      This is one of the routines applied to the entire configuration
141  *      tree.
142  *
143  * Side Effects:
144  *      May exit from package.
145  *------------------------------------------------------------------------*/
146
147 int
148 check(register CTREEPTR np, char *path)
149 {                               /*check */
150
151     register CTREEPTR np2;      /*Node ptr for np's child */
152     register struct dirent *de; /*Ptr to directory entry */
153     DIR *dp;                    /*Ptr to directory being read */
154     struct stat stb;            /*Stat block for path2 */
155     int retval;                 /*Return value */
156
157     retval = 0;
158     debug_message("[check] Checking pathname %s", path, np);
159
160     if ((np->flag & F_TYPE) && (np->updtspec & U_LOSTFOUND))
161         CheckMount(path);
162     if (!(np->flag & F_PROTO)) {
163         if (!(np->flag & F_TYPE))
164             fatal("** Incomplete: %s", path);
165         else if ((np->type == S_IFCHR) || (np->type == S_IFBLK))
166             /* $$Note: Actually, the parser takes care of this */
167             fatal("** No device numbers specified for device %s\n", path);
168         return (retval);
169     }
170
171     /*
172      * No checks needed for character & block special devices (& sockets
173      * & named pipes).
174      */
175     if ((np->type == S_IFCHR)
176         || (np->type == S_IFBLK)
177 #ifndef AFS_AIX_ENV
178         || (np->type == S_IFSOCK)
179 #endif /* AFS_AIX_ENV */
180 #ifdef S_IFIFO
181         || (np->type == S_IFIFO)
182 #endif /* S_IFIFO */
183         )
184         return (retval);
185
186     /*
187      * Construct the target path, either absolute or prefixed.
188      */
189     if (np->updtspec & U_ABSPATH)
190         /*
191          * Absolute path.
192          */
193         sprintf(path2, "%s", np->proto.info.path);
194     else
195         /*
196          * Prefixed path.
197          */
198         sprintf(path2, "%s%s", np->proto.info.path, path);
199
200     debug_message("[check] Statting %s", path2);
201
202     /*
203      * If this is a symlink, lstat the guy and warn people if it
204      * isn't found.
205      */
206     if ((np->flag & F_TYPE) && (np->type == S_IFLNK)) {
207         if (lstat(path2, &stb) < 0)
208             verbose_message("* Warning: symlink %s not found", path2);
209         return (retval);
210     }
211
212     /*
213      * Do a normal stat, failing if it does.
214      */
215     if (stat(path2, &stb) < 0)
216         fatal("** Stat failed for %s; %m", path2);
217
218     if (np->flag & F_TYPE) {
219         if (np->type == S_IFLNK)
220             return (retval);
221         if ((stb.st_mode & S_IFMT) != np->type)
222             fatal("** Type conflict for %s", path2);
223     } else {
224         np->type = stb.st_mode & S_IFMT;
225         np->flag |= F_TYPE;
226     }
227
228     if (!(np->flag & F_MODE)) {
229         /*Fill in the mode (protection) info from the stat block */
230         np->mode |= stb.st_mode & ~S_IFMT;
231         np->flag |= F_MODE;
232     }
233
234     if (!(np->flag & F_UID)) {
235         /*Fill in the user info from the stat block */
236         np->uid = stb.st_uid;
237         np->flag |= F_UID;
238     }
239
240     if (!(np->flag & F_GID)) {
241         /*Fill in the group info from the stat block */
242 #ifdef  VICE
243         np->gid = (stb.st_gid == 32767) ? 0 : stb.st_gid;
244 #else /* VICE */
245         np->gid = stb.st_gid;
246 #endif /* VICE */
247         np->flag |= F_GID;
248     }
249
250     if (!(np->flag & F_MTIME)) {
251         /*Fill in the last modified time from the stat block */
252         np->mtime = stb.st_mtime;
253         np->flag |= F_MTIME;
254     }
255
256     /*
257      * If we've reached a non-directory, we're all done.
258      */
259     if (np->type != S_IFDIR) {
260         debug_message("[check] Reached leaf node, returning");
261         return (retval);
262     }
263
264     /*
265      * Open up the directory and sweep through it, checking all its
266      * entries.
267      */
268     verbose_message("Scanning directory %s", path2);
269     if ((dp = opendir(path2)) == 0)
270         fatal("** Opendir failed on %s; %m", path2);
271
272     while ((de = readdir(dp)) != 0) {
273         if (de->d_name[0] == '.') {
274             /*
275              * Don't process dot, the current directory, or dotdot, the
276              * parent.
277              */
278             if (de->d_name[1] == 0)
279                 continue;
280             if (de->d_name[1] == '.' && de->d_name[2] == 0)
281                 continue;
282         }
283
284         if ((np2 =
285              LocateChildNode(np, de->d_name, C_LOCATE | C_CREATE)) == NULL)
286             fatal("** Bad path: %s/%s", path, de->d_name);
287
288         if (!(np2->flag & F_PROTO)) {
289             if (np->updtspec & U_ABSPATH) {
290                 np2->updtspec |= U_ABSPATH;
291                 sprintf(path3, "%s/%s", np->proto.info.path, de->d_name);
292                 np2->proto.info.path = emalloc((unsigned)(strlen(path3) + 1));
293                 (void)strcpy(np2->proto.info.path, path3);
294             } else {
295                 np2->proto.info.path = np->proto.info.path;
296             }
297             np2->flag |= F_PROTO;
298             continue;
299         }
300         if ((np2->updtspec & U_ABSPATH) != (np->updtspec & U_ABSPATH))
301             fatal
302                 ("** Prototype conflict: %s/%s: Absolute & relative paths given",
303                  path, de->d_name);
304         if (np->updtspec & U_ABSPATH) {
305             sprintf(path3, "%s/%s", np->proto, de->d_name);
306             if (strcmp(path3, np2->proto.info.path))
307                 fatal("** Prototype conflict: %s/%s: Previously %s", path,
308                       de->d_name, np2->proto.info.path);
309         } else {
310             if (strcmp(np->proto.info.path, np2->proto.info.path))
311                 fatal("** Prototype conflict: %s/%s: Mismatch for %s and %s",
312                       path, de->d_name, np->proto.info.path,
313                       np2->proto.info.path);
314         }
315     }                           /*For each directory entry */
316
317     /*
318      * Make sure to close the directory before we leave.
319      */
320     (void)closedir(dp);
321     return (retval);
322 }                               /*check */