1 #ifndef _WIN32
2 #define _GNU_SOURCE
3 #define _DEFAULT_SOURCE
4 #define _BSD_SOURCE
5 #include <unistd.h>
6 #include <fcntl.h>
7 #include <dirent.h>
8 #else
9 #include "vccompat.hpp"
10 #ifdef __MINGW32__
11 #include <dirent.h>
12 #include <fcntl.h>
13 #include <unistd.h>
14 #endif
15 #endif
16 
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <assert.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <string.h>
24 
25 #include "pocl.h"
26 #include "pocl_debug.h"
27 #include "pocl_file_util.h"
28 
29 #ifdef __ANDROID__
30 
31 int pocl_mkstemp(char *path);
32 
33 #endif
34 
35 /*****************************************************************************/
36 
37 int
pocl_rm_rf(const char * path)38 pocl_rm_rf(const char* path)
39 {
40   DIR *d = opendir(path);
41   size_t path_len = strlen(path);
42   int error = -1;
43 
44   if(d)
45     {
46       struct dirent *p = readdir(d);
47       error = 0;
48       while (!error && p)
49         {
50           char *buf;
51           if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, ".."))
52             {
53               p = readdir (d);
54               continue;
55             }
56 
57           size_t len = path_len + strlen(p->d_name) + 2;
58           buf = malloc(len);
59           if (buf)
60             {
61               struct stat statbuf;
62               snprintf(buf, len, "%s/%s", path, p->d_name);
63 
64               if (stat (buf, &statbuf) < 0)
65                 POCL_ABORT ("Can't get stat() on %s\n", buf);
66               if (S_ISDIR (statbuf.st_mode))
67                 error = pocl_rm_rf (buf);
68               if (S_ISREG (statbuf.st_mode) || S_ISLNK (statbuf.st_mode))
69                 error = remove (buf);
70 
71               free(buf);
72             }
73           else
74             POCL_ABORT ("out of memory");
75 
76           p = readdir(d);
77         }
78       closedir(d);
79 
80       if (!error)
81         error = remove (path);
82     }
83   return error;
84 }
85 
86 
87 int
pocl_mkdir_p(const char * path)88 pocl_mkdir_p (const char* path)
89 {
90   size_t len = strlen (path);
91   if (len >= POCL_FILENAME_LENGTH - 1)
92     {
93       return -1;
94     }
95   if (len <= 1)
96     return -1;
97 
98   char path_copy[POCL_FILENAME_LENGTH];
99   memcpy (path_copy, path, len);
100   path_copy[len] = 0;
101 
102   for (char *tmp = path_copy + 1; *tmp; tmp++)
103     {
104       if (*tmp == '/')
105         {
106           *tmp = '\0';
107           errno = 0;
108           if (mkdir (path_copy, S_IRWXU) != 0)
109             {
110               if (errno != EEXIST)
111                 return -1;
112             }
113           *tmp = '/';
114         }
115     }
116 
117   if (mkdir (path_copy, S_IRWXU) != 0)
118     {
119       if (errno != EEXIST)
120         return -1;
121     }
122 
123   return 0;
124 }
125 
126 int
pocl_remove(const char * path)127 pocl_remove(const char* path)
128 {
129   return remove(path);
130 }
131 
132 int
pocl_exists(const char * path)133 pocl_exists(const char* path)
134 {
135   return !access(path, R_OK);
136 }
137 
138 int
pocl_touch_file(const char * path)139 pocl_touch_file(const char* path)
140 {
141   FILE *f = fopen(path, "w");
142   if (f)
143     {
144       fclose(f);
145       return 0;
146     }
147   return -1;
148 }
149 /****************************************************************************/
150 
151 #define CHUNK_SIZE (2 * 1024 * 1024)
152 
153 int
pocl_read_file(const char * path,char ** content,uint64_t * filesize)154 pocl_read_file(const char* path, char** content, uint64_t *filesize)
155 {
156   assert(content);
157   assert(path);
158   assert(filesize);
159   *content = NULL;
160   *filesize = 0;
161 
162   /* files in /proc return zero size, while
163      files in /sys return size larger than actual actual content size;
164      this reads the content sequentially. */
165   size_t total_size = 0;
166   size_t actually_read = 0;
167   char *ptr = (char *)malloc (CHUNK_SIZE + 1);
168   if (ptr == NULL)
169     return -1;
170 
171   FILE *f = fopen (path, "r");
172   if (f == NULL) {
173     POCL_MSG_ERR ("fopen( %s ) failed\n", path);
174     goto ERROR;
175   }
176 
177   do
178     {
179       char *reallocated = (char *)realloc (ptr, (total_size + CHUNK_SIZE + 1));
180       if (reallocated == NULL)
181         goto ERROR;
182       ptr = reallocated;
183 
184       actually_read = fread (ptr + total_size, 1, CHUNK_SIZE, f);
185       total_size += actually_read;
186     }
187   while (actually_read == CHUNK_SIZE);
188 
189   if (ferror (f))
190     goto ERROR;
191 
192   if (fclose (f))
193     goto ERROR;
194 
195   /* add an extra NULL character for strings */
196   ptr[total_size] = 0;
197   *content = ptr;
198   *filesize = (uint64_t)total_size;
199   return 0;
200 
201 ERROR:
202   free (ptr);
203   return -1;
204 }
205 
206 /* Atomic write - with rename() */
207 int
pocl_write_file(const char * path,const char * content,uint64_t count,int append,int dont_rewrite)208 pocl_write_file (const char *path, const char *content, uint64_t count,
209                  int append, int dont_rewrite)
210 {
211   assert(path);
212   assert(content);
213   char path2[POCL_FILENAME_LENGTH];
214   int err, fd = -1;
215 
216   if (pocl_exists(path))
217     {
218       if (dont_rewrite)
219         {
220           if (!append)
221             return 0;
222         }
223       else
224         {
225           int res = pocl_remove(path);
226           if (res)
227             {
228               POCL_MSG_ERR ("pocl_remove(%s) failed\n", path);
229               return res;
230             }
231         }
232     }
233 
234   if (append)
235     {
236       fd = open (path, O_RDWR | O_APPEND | O_CREAT, 0660);
237       err = fd < 0;
238     }
239   else
240     {
241       err = pocl_mk_tempname (path2, path, ".temp", &fd);
242     }
243 
244   if (err)
245     {
246       POCL_MSG_ERR ("open(%s) failed\n", path);
247       return -1;
248     }
249 
250   ssize_t res = write (fd, content, (size_t)count);
251   if (res < 0 || (size_t)res < (size_t)count)
252     {
253       POCL_MSG_ERR ("write(%s) failed\n", path);
254       return -1;
255     }
256 
257 #ifdef HAVE_FDATASYNC
258   if (fdatasync (fd))
259     {
260       POCL_MSG_ERR ("fdatasync() failed\n");
261       return errno;
262     }
263 #elif defined(HAVE_FSYNC)
264   if (fsync (fd))
265     {
266       POCL_MSG_ERR ("fsync() failed\n");
267       return errno;
268     }
269 #endif
270 
271   if (close (fd) < 0)
272     return errno;
273 
274   if (append)
275     return 0;
276   else
277     return pocl_rename (path2, path);
278 }
279 
280 /****************************************************************************/
281 
pocl_rename(const char * oldpath,const char * newpath)282 int pocl_rename(const char *oldpath, const char *newpath) {
283   return rename (oldpath, newpath);
284 }
285 
286 int
pocl_mk_tempname(char * output,const char * prefix,const char * suffix,int * ret_fd)287 pocl_mk_tempname (char *output, const char *prefix, const char *suffix,
288                   int *ret_fd)
289 {
290 #if defined(_WIN32)
291   char buf[256];
292   int ok = GetTempFileName(getenv("TEMP"), prefix, 0, buf);
293   return ok ? 0 : 1;
294 #elif defined(HAVE_MKOSTEMPS) || defined(HAVE_MKSTEMPS) || defined(__ANDROID__)
295   /* using mkstemp() instead of tmpnam() has no real benefit
296    * here, as we have to pass the filename to llvm,
297    * but tmpnam() generates an annoying warning... */
298   int fd;
299 
300   strncpy (output, prefix, POCL_FILENAME_LENGTH);
301   size_t len = strlen (prefix);
302   strncpy (output + len, "_XXXXXX", (POCL_FILENAME_LENGTH - len));
303 
304 #ifdef __ANDROID__
305   fd = pocl_mkstemp (output);
306 #else
307   if (suffix)
308     {
309       len += 7;
310       strncpy (output + len, suffix, (POCL_FILENAME_LENGTH - len));
311 #ifdef HAVE_MKOSTEMPS
312       fd = mkostemps (output, strlen (suffix), O_CLOEXEC);
313 #else
314       fd = mkstemps (output, strlen (suffix));
315 #endif
316     }
317   else
318 #ifdef HAVE_MKOSTEMPS
319     fd = mkostemp (output, O_CLOEXEC);
320 #else
321     fd = mkstemp (output);
322 #endif
323 #endif
324 
325   if (fd < 0)
326     {
327       POCL_MSG_ERR ("mkstemp() failed\n");
328       return errno;
329     }
330 
331   int err = 0;
332   if (ret_fd)
333     *ret_fd = fd;
334   else
335     err = close (fd);
336 
337   return err ? errno : 0;
338 
339 #else
340 #error mkostemps() / mkstemps() both unavailable
341 #endif
342 }
343 
344 int
pocl_mk_tempdir(char * output,const char * prefix)345 pocl_mk_tempdir (char *output, const char *prefix)
346 {
347 #if defined(_WIN32)
348   assert (0);
349 #elif defined(HAVE_MKDTEMP)
350   /* TODO mkdtemp() might not be portable outside Linux */
351   strncpy (output, prefix, POCL_FILENAME_LENGTH);
352   size_t len = strlen (prefix);
353   strncpy (output + len, "_XXXXXX", (POCL_FILENAME_LENGTH - len));
354   return (mkdtemp (output) == NULL);
355 #else
356 #error mkdtemp() not available
357 #endif
358 }
359 
360 /* write content[count] into a temporary file, and return the tempfile name in
361  * output_path */
362 int
pocl_write_tempfile(char * output_path,const char * prefix,const char * suffix,const char * content,unsigned long count,int * ret_fd)363 pocl_write_tempfile (char *output_path, const char *prefix, const char *suffix,
364                      const char *content, unsigned long count, int *ret_fd)
365 {
366   assert (output_path);
367   assert (prefix);
368   assert (suffix);
369   assert (content);
370 
371   int fd = -1, err = 0;
372 
373   err = pocl_mk_tempname (output_path, prefix, suffix, &fd);
374   if (err)
375     {
376       POCL_MSG_ERR ("pocl_mk_tempname() failed\n");
377       return err;
378     }
379 
380   size_t bytes = count;
381   ssize_t res;
382   while (bytes > 0)
383     {
384       res = write (fd, content, bytes);
385       if (res < 0)
386         {
387           POCL_MSG_ERR ("write(%s) failed\n", output_path);
388           return errno;
389         }
390       else
391         {
392           bytes -= res;
393           content += res;
394         }
395     }
396 
397 #ifdef HAVE_FDATASYNC
398   if (fdatasync (fd))
399     {
400       POCL_MSG_ERR ("fdatasync() failed\n");
401       return errno;
402     }
403 #elif defined(HAVE_FSYNC)
404   if (fsync (fd))
405     return errno;
406 #endif
407 
408   err = 0;
409   if (ret_fd)
410     *ret_fd = fd;
411   else
412     err = close (fd);
413 
414   return err ? errno : 0;
415 }
416