xref: /petsc/src/sys/fileio/ftest.c (revision fbf9dbe564678ed6eff1806adbc4c4f01b9743f4)
1 
2 #include <petscsys.h>
3 #include <errno.h>
4 #if defined(PETSC_HAVE_PWD_H)
5   #include <pwd.h>
6 #endif
7 #include <ctype.h>
8 #include <sys/stat.h>
9 #if defined(PETSC_HAVE_UNISTD_H)
10   #include <unistd.h>
11 #endif
12 #if defined(PETSC_HAVE_SYS_UTSNAME_H)
13   #include <sys/utsname.h>
14 #endif
15 #if defined(PETSC_HAVE_IO_H)
16   #include <io.h>
17 #endif
18 #if defined(PETSC_HAVE_SYS_SYSTEMINFO_H)
19   #include <sys/systeminfo.h>
20 #endif
21 
22 #if defined(PETSC_HAVE__ACCESS) || defined(PETSC_HAVE_ACCESS)
23 
24   #include <errno.h>
25 static PetscErrorCode PetscTestOwnership(const char fname[], char mode, uid_t fuid, gid_t fgid, int fmode, PetscBool *flg)
26 {
27   int m = R_OK;
28 
29   PetscFunctionBegin;
30   if (mode == 'r') m = R_OK;
31   else if (mode == 'w') m = W_OK;
32   else if (mode == 'x') m = X_OK;
33   else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mode must be one of r, w, or x");
34   #if defined(PETSC_HAVE_ACCESS)
35   if (!access(fname, m)) {
36     PetscCall(PetscInfo(NULL, "System call access() succeeded on file %s\n", fname));
37     *flg = PETSC_TRUE;
38   } else {
39     PetscCall(PetscInfo(NULL, "System call access() failed on file %s due to \"%s\"\n", fname, strerror(errno)));
40     *flg = PETSC_FALSE;
41   }
42   #else
43   PetscCheck(m != X_OK, PETSC_COMM_SELF, PETSC_ERR_SUP, "Unable to check execute permission for file %s", fname);
44   if (!_access(fname, m)) *flg = PETSC_TRUE;
45   #endif
46   PetscFunctionReturn(PETSC_SUCCESS);
47 }
48 
49 #else /* PETSC_HAVE_ACCESS or PETSC_HAVE__ACCESS */
50 
51 static PetscErrorCode PetscTestOwnership(const char fname[], char mode, uid_t fuid, gid_t fgid, int fmode, PetscBool *flg)
52 {
53   uid_t  uid;
54   gid_t *gid = NULL;
55   int    numGroups;
56   int    rbit = S_IROTH;
57   int    wbit = S_IWOTH;
58   int    ebit = S_IXOTH;
59   #if !defined(PETSC_MISSING_GETGROUPS)
60   int    err;
61   #endif
62 
63   PetscFunctionBegin;
64   /* Get the number of supplementary group IDs */
65   #if !defined(PETSC_MISSING_GETGROUPS)
66   numGroups = getgroups(0, gid);
67   PetscCheck(numGroups >= 0, PETSC_COMM_SELF, PETSC_ERR_SYS, "Unable to count supplementary group IDs due to \"%s\"", strerror(errno));
68   PetscCall(PetscMalloc1(numGroups + 1, &gid));
69   #else
70   numGroups = 0;
71   #endif
72 
73   /* Get the (effective) user and group of the caller */
74   uid    = geteuid();
75   gid[0] = getegid();
76 
77   /* Get supplementary group IDs */
78   #if !defined(PETSC_MISSING_GETGROUPS)
79   err = getgroups(numGroups, gid + 1);
80   PetscCheck(err >= 0, PETSC_COMM_SELF, PETSC_ERR_SYS, "Unable to obtain supplementary group IDs due to \"%s\"", strerror(errno));
81   #endif
82 
83   /* Test for accessibility */
84   if (fuid == uid) {
85     rbit = S_IRUSR;
86     wbit = S_IWUSR;
87     ebit = S_IXUSR;
88   } else {
89     int g;
90 
91     for (g = 0; g <= numGroups; g++) {
92       if (fgid == gid[g]) {
93         rbit = S_IRGRP;
94         wbit = S_IWGRP;
95         ebit = S_IXGRP;
96         break;
97       }
98     }
99   }
100   PetscCall(PetscFree(gid));
101 
102   if (mode == 'r') {
103     if (fmode & rbit) *flg = PETSC_TRUE;
104   } else if (mode == 'w') {
105     if (fmode & wbit) *flg = PETSC_TRUE;
106   } else if (mode == 'x') {
107     if (fmode & ebit) *flg = PETSC_TRUE;
108   }
109   PetscFunctionReturn(PETSC_SUCCESS);
110 }
111 
112 #endif /* PETSC_HAVE_ACCESS */
113 
114 static PetscErrorCode PetscGetFileStat(const char fname[], uid_t *fileUid, gid_t *fileGid, int *fileMode, PetscBool *exists)
115 {
116   struct stat statbuf;
117   int         ierr;
118 
119   PetscFunctionBegin;
120   *fileMode = 0;
121   *exists   = PETSC_FALSE;
122 #if defined(PETSC_HAVE_STAT_NO_CONST)
123   ierr = stat((char *)fname, &statbuf);
124 #else
125   ierr = stat(fname, &statbuf);
126 #endif
127   if (ierr) {
128 #if defined(EOVERFLOW)
129     PetscCheck(errno != EOVERFLOW, PETSC_COMM_SELF, PETSC_ERR_SYS, "EOVERFLOW in stat(), configure PETSc --with-large-file-io=1 to support files larger than 2GiB");
130 #endif
131     PetscCall(PetscInfo(NULL, "System call stat() failed on file %s due to \"%s\"\n", fname, strerror(errno)));
132     *exists = PETSC_FALSE;
133   } else {
134     PetscCall(PetscInfo(NULL, "System call stat() succeeded on file %s\n", fname));
135     *exists   = PETSC_TRUE;
136     *fileUid  = statbuf.st_uid;
137     *fileGid  = statbuf.st_gid;
138     *fileMode = statbuf.st_mode;
139   }
140   PetscFunctionReturn(PETSC_SUCCESS);
141 }
142 
143 /*@C
144    PetscTestFile - checks for the existence of a file
145 
146    Not Collective
147 
148    Input Parameters:
149 +  fname - the filename
150 -  mode - either 'r', 'w', 'x' or '\0'
151 
152    Output Parameter:
153 .  flg - the file exists and satisfies the mode
154 
155    Level: intermediate
156 
157    Note:
158    If mode is '\0', no permissions checks are performed
159 
160 .seealso: `PetscTestDirectory()`, `PetscLs()`
161 @*/
162 PetscErrorCode PetscTestFile(const char fname[], char mode, PetscBool *flg)
163 {
164   uid_t     fuid;
165   gid_t     fgid;
166   int       fmode;
167   PetscBool exists;
168 
169   PetscFunctionBegin;
170   *flg = PETSC_FALSE;
171   if (!fname) PetscFunctionReturn(PETSC_SUCCESS);
172 
173   PetscCall(PetscGetFileStat(fname, &fuid, &fgid, &fmode, &exists));
174   if (!exists) PetscFunctionReturn(PETSC_SUCCESS);
175   /* Except for systems that have this broken stat macros (rare), this is the correct way to check for a regular file */
176   if (!S_ISREG(fmode)) PetscFunctionReturn(PETSC_SUCCESS);
177   /* return if asked to check for existence only */
178   if (mode == '\0') {
179     *flg = exists;
180     PetscFunctionReturn(PETSC_SUCCESS);
181   }
182   PetscCall(PetscTestOwnership(fname, mode, fuid, fgid, fmode, flg));
183   PetscFunctionReturn(PETSC_SUCCESS);
184 }
185 
186 /*@C
187    PetscTestDirectory - checks for the existence of a directory
188 
189    Not Collective
190 
191    Input Parameters:
192 +  dirname - the directory name
193 -  mode - either 'r', 'w', or 'x'
194 
195    Output Parameter:
196 .  flg - the directory exists and satisfies the mode
197 
198    Level: intermediate
199 
200 .seealso: `PetscTestFile()`, `PetscLs()`, `PetscRMTree()`
201 @*/
202 PetscErrorCode PetscTestDirectory(const char dirname[], char mode, PetscBool *flg)
203 {
204   uid_t     fuid;
205   gid_t     fgid;
206   int       fmode;
207   PetscBool exists;
208 
209   PetscFunctionBegin;
210   *flg = PETSC_FALSE;
211   if (!dirname) PetscFunctionReturn(PETSC_SUCCESS);
212 
213   PetscCall(PetscGetFileStat(dirname, &fuid, &fgid, &fmode, &exists));
214   if (!exists) PetscFunctionReturn(PETSC_SUCCESS);
215   /* Except for systems that have this broken stat macros (rare), this
216      is the correct way to check for a directory */
217   if (!S_ISDIR(fmode)) PetscFunctionReturn(PETSC_SUCCESS);
218 
219   PetscCall(PetscTestOwnership(dirname, mode, fuid, fgid, fmode, flg));
220   PetscFunctionReturn(PETSC_SUCCESS);
221 }
222 
223 /*@C
224    PetscLs - produce a listing of the files in a directory
225 
226    Collective
227 
228    Input Parameters:
229 +  comm - the MPI communicator
230 .  dirname - the directory name
231 -  tlen - the length of the buffer `found`
232 
233    Output Parameters:
234 +  found - listing of files
235 -  flg - the directory exists
236 
237    Level: intermediate
238 
239 .seealso: `PetscTestFile()`, `PetscRMTree()`, `PetscTestDirectory()`
240 @*/
241 PetscErrorCode PetscLs(MPI_Comm comm, const char dirname[], char found[], size_t tlen, PetscBool *flg)
242 {
243   size_t len;
244   char  *f, program[PETSC_MAX_PATH_LEN];
245   FILE  *fp;
246 
247   PetscFunctionBegin;
248   PetscCall(PetscStrncpy(program, "ls ", sizeof(program)));
249   PetscCall(PetscStrlcat(program, dirname, sizeof(program)));
250 #if defined(PETSC_HAVE_POPEN)
251   PetscCall(PetscPOpen(comm, NULL, program, "r", &fp));
252 #else
253   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "Cannot run external programs on this machine");
254 #endif
255   f = fgets(found, tlen, fp);
256   if (f) *flg = PETSC_TRUE;
257   else *flg = PETSC_FALSE;
258   while (f) {
259     PetscCall(PetscStrlen(found, &len));
260     f = fgets(found + len, tlen - len, fp);
261   }
262   if (*flg) PetscCall(PetscInfo(NULL, "ls on %s gives \n%s\n", dirname, found));
263 #if defined(PETSC_HAVE_POPEN)
264   PetscCall(PetscPClose(comm, fp));
265 #else
266   SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP_SYS, "Cannot run external programs on this machine");
267 #endif
268   PetscFunctionReturn(PETSC_SUCCESS);
269 }
270