"Fossies" - the Fresh Open Source Software Archive 
Member "eucalyptus-4.4.2/util/euca_file.c" (4 Aug 2017, 33525 Bytes) of package /linux/misc/eucalyptus-4.4.2.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "euca_file.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
4.4.1_vs_4.4.2.
1 // -*- mode: C; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-
2 // vim: set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
3
4 /*************************************************************************
5 * Copyright 2009-2012 Eucalyptus Systems, Inc.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 3 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see http://www.gnu.org/licenses/.
18 *
19 * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
20 * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
21 * additional information or have any questions.
22 *
23 * This file may incorporate work covered under the following copyright
24 * and permission notice:
25 *
26 * Software License Agreement (BSD License)
27 *
28 * Copyright (c) 2008, Regents of the University of California
29 * All rights reserved.
30 *
31 * Redistribution and use of this software in source and binary forms,
32 * with or without modification, are permitted provided that the
33 * following conditions are met:
34 *
35 * Redistributions of source code must retain the above copyright
36 * notice, this list of conditions and the following disclaimer.
37 *
38 * Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer
40 * in the documentation and/or other materials provided with the
41 * distribution.
42 *
43 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
44 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
45 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
46 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
47 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
48 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
49 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
50 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
51 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
53 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
54 * POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE
55 * THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL,
56 * COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE,
57 * AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
58 * IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA,
59 * SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY,
60 * WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION,
61 * REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO
62 * IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT
63 * NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS.
64 ************************************************************************/
65
66 //!
67 //! @file util/euca_file.h
68 //! This file implements the various file and directory utility functions
69 //!
70
71 /*----------------------------------------------------------------------------*\
72 | |
73 | INCLUDES |
74 | |
75 \*----------------------------------------------------------------------------*/
76
77 #define _FILE_OFFSET_BITS 64 // so large-file support works on 32-bit systems
78 #include <stdio.h>
79 #include <stdlib.h>
80 #define _GNU_SOURCE
81 #include <string.h> // strlen, strcpy
82 #include <ctype.h> // isspace
83 #include <assert.h>
84 #include <stdarg.h>
85 #include <sys/types.h>
86 #include <sys/stat.h>
87 #include <sys/vfs.h>
88 #include <unistd.h>
89 #include <time.h>
90 #include <math.h> // powf
91 #include <fcntl.h> // open
92 #include <utime.h> // utime
93 #include <sys/wait.h>
94 #include <pwd.h>
95 #include <dirent.h> // opendir, etc
96 #include <sys/errno.h> // errno
97 #include <sys/time.h> // gettimeofday
98 #include <limits.h>
99 #include <sys/mman.h> // mmap
100 #include <pthread.h>
101
102 #include "eucalyptus.h"
103
104 #include <diskutil.h>
105
106 #include "misc.h"
107 #include "euca_string.h"
108 #include "euca_file.h"
109
110 /*----------------------------------------------------------------------------*\
111 | |
112 | DEFINES |
113 | |
114 \*----------------------------------------------------------------------------*/
115
116 #define BUFSIZE 1024
117
118 /*----------------------------------------------------------------------------*\
119 | |
120 | TYPEDEFS |
121 | |
122 \*----------------------------------------------------------------------------*/
123
124 /*----------------------------------------------------------------------------*\
125 | |
126 | ENUMERATIONS |
127 | |
128 \*----------------------------------------------------------------------------*/
129
130 /*----------------------------------------------------------------------------*\
131 | |
132 | STRUCTURES |
133 | |
134 \*----------------------------------------------------------------------------*/
135
136 /*----------------------------------------------------------------------------*\
137 | |
138 | EXTERNAL VARIABLES |
139 | |
140 \*----------------------------------------------------------------------------*/
141
142 /* Should preferably be handled in header file */
143
144 /*----------------------------------------------------------------------------*\
145 | |
146 | GLOBAL VARIABLES |
147 | |
148 \*----------------------------------------------------------------------------*/
149
150 /*----------------------------------------------------------------------------*\
151 | |
152 | STATIC VARIABLES |
153 | |
154 \*----------------------------------------------------------------------------*/
155
156 /*----------------------------------------------------------------------------*\
157 | |
158 | STATIC PROTOTYPES |
159 | |
160 \*----------------------------------------------------------------------------*/
161
162 /*----------------------------------------------------------------------------*\
163 | |
164 | MACROS |
165 | |
166 \*----------------------------------------------------------------------------*/
167
168 /*----------------------------------------------------------------------------*\
169 | |
170 | IMPLEMENTATION |
171 | |
172 \*----------------------------------------------------------------------------*/
173
174 //!
175 //! Remove a directory given by the psPath string pointer. If the 'force' parameter
176 //! is set to TRUE, the directory will be emptied and deleted. If the 'force'
177 //! parameter is set to FALSE then this is the equivalent of an 'rmdir()' system
178 //! calls which will not delete a non-empty directory.
179 //!
180 //! @param[in] psPath a constant pointer to the string containing the path to the directory to remove.
181 //! @param[in] force set to TRUE, will force remove a non-empty directory
182 //!
183 //! @return EUCA_OK on success or the following error code:
184 //! EUCA_INVALID_ERROR if any of our pre-conditions are not met
185 //! EUCA_SYSTEM_ERROR if the system fails to remove a file or directory in the process
186 //! EUCA_MEMORY_ERROR if we fail to allocate memory during the process
187 //!
188 //! @see
189 //!
190 //! @pre \li psPath must not be NULL
191 //! \li psPath must be a valid path to a directory
192 //! \li if force is set to FALSE, the directory should be empty
193 //!
194 //! @post \li on success, the directory will be removed
195 //! \li on failure, the directory may remain with some content
196 //!
197 //! @note
198 //!
199 int euca_rmdir(const char *psPath, boolean force)
200 {
201 int result = EUCA_OK;
202 char *psBuf = NULL;
203 DIR *pDir = NULL;
204 size_t len = 0;
205 size_t pathLen = 0;
206 struct dirent *pDirEnt = NULL;
207 struct stat statbuf = { 0 };
208
209 // Make sure we have a path
210 if (psPath == NULL) {
211 return (EUCA_INVALID_ERROR);
212 }
213 // If force was set, read the directory and empty it
214 if (force) {
215 // Retrieve the length of our directory path
216 pathLen = strlen(psPath);
217
218 // Open the directory and start scanning for items
219 if ((pDir = opendir(psPath)) != NULL) {
220 while ((result == EUCA_OK) && ((pDirEnt = readdir(pDir)) != NULL)) {
221 // Skip the names "." and ".." as we don't want to recurse on them.
222 if (!strcmp(pDirEnt->d_name, ".") || !strcmp(pDirEnt->d_name, "..")) {
223 continue;
224 }
225
226 len = pathLen + strlen(pDirEnt->d_name) + 2;
227 if ((psBuf = EUCA_ALLOC(len, sizeof(char))) != NULL) {
228
229 snprintf(psBuf, len, "%s/%s", psPath, pDirEnt->d_name);
230
231 if (stat(psBuf, &statbuf) == 0) {
232 if (S_ISDIR(statbuf.st_mode)) {
233 result = euca_rmdir(psBuf, TRUE);
234 } else {
235 if (unlink(psBuf) != 0) {
236 result = EUCA_SYSTEM_ERROR;
237 }
238 }
239 }
240
241 EUCA_FREE(psBuf);
242 } else {
243 // Memory failure
244 result = EUCA_MEMORY_ERROR;
245 }
246 }
247
248 closedir(pDir);
249 } else {
250 // return the proper error
251 if (errno == ENOTDIR)
252 return (EUCA_INVALID_ERROR);
253 return (EUCA_SYSTEM_ERROR);
254 }
255 }
256 // If we were successful so far, remove the directory
257 if (result == EUCA_OK) {
258 if (rmdir(psPath) != 0) {
259 // Set the proper return error
260 if (errno == ENOTDIR)
261 return (EUCA_INVALID_ERROR);
262 return (EUCA_SYSTEM_ERROR);
263 }
264 }
265
266 return (result);
267 }
268
269 //!
270 //! Sanitize a path string and make sure it does not contains any illegal characters
271 //!
272 //! @param[in] psPath a pointer to a string containing the path to sanitize
273 //!
274 //! @return EUCA_OK if the path is a valid string and EUCA_ERROR if its not. If NULL is passed
275 //! this function will return EUCA_INVALID_ERROR.
276 //!
277 //! @note a valid path should not contain any of the following characters: !@#$%^&*(...
278 //!
279 int euca_sanitize_path(const char *psPath)
280 {
281 static char sOkChar[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890/._-:";
282
283 // Check if we have a path provided
284 if (psPath) {
285 // Does the path contains only legal characters?
286 if (strlen(psPath) != strspn(psPath, sOkChar))
287 return (EUCA_ERROR);
288 return (EUCA_OK);
289 }
290 return (EUCA_INVALID_ERROR);
291 }
292
293 //!
294 //! make sure 'dir' is a directory or a soft-link to one
295 //! and that it is readable by the current user (1 on error)
296 //!
297 //! @param[in] dir the directory name to validate
298 //!
299 //! @return 0 if dir is a directory or 1 if not
300 //!
301 //! @pre The dir field must not be NULL
302 //!
303 int check_directory(const char *dir)
304 {
305 int rc = 0;
306 DIR *d = NULL;
307 char checked_dir[EUCA_MAX_PATH] = "";
308 struct stat mystat = { 0 };
309
310 if (!dir) {
311 return (1);
312 }
313
314 snprintf(checked_dir, sizeof(checked_dir), "%s", dir);
315
316 if ((rc = lstat(checked_dir, &mystat)) < 0)
317 return (1);
318
319 // if a soft link, append '/' and try lstat() again
320 if (!S_ISDIR(mystat.st_mode) && S_ISLNK(mystat.st_mode)) {
321 snprintf(checked_dir, sizeof(checked_dir), "%s/", dir);
322 if ((rc = lstat(checked_dir, &mystat)) < 0)
323 return (1);
324 }
325
326 if (!S_ISDIR(mystat.st_mode))
327 return (1);
328
329 if ((d = opendir(checked_dir)) == NULL)
330 return (1);
331
332 closedir(d);
333 return (0);
334 }
335
336 //!
337 //! Check if a file is newer than the given timestamp
338 //!
339 //! @param[in] file the file name string
340 //! @param[in] mtime the timestamp to compare with
341 //!
342 //! @return 0 if the file is newer than timestamp or 1 if not
343 //!
344 int check_file_newer_than(const char *file, time_t mtime)
345 {
346 int rc = 0;
347 struct stat mystat = { 0 };
348
349 if (!file) {
350 return (1);
351 } else if (mtime <= 0) {
352 return (0);
353 }
354
355 bzero(&mystat, sizeof(struct stat));
356 if ((rc = stat(file, &mystat)) != 0) {
357 return (1);
358 }
359
360 if (mystat.st_mtime > mtime) {
361 return (0);
362 }
363
364 return (1);
365 }
366
367 //!
368 //!
369 //!
370 //! @param[in] file
371 //!
372 //! @return 0 on success or 1 on failure
373 //!
374 int check_block(const char *file)
375 {
376 int rc = 0;
377 char *rpath = NULL;
378 struct stat mystat = { 0 };
379
380 if (!file) {
381 LOGERROR("Invalid file name");
382 return (1);
383 }
384
385 if ((rpath = realpath(file, NULL)) == NULL) {
386 LOGERROR("No canonical file found for %s", file);
387 return (1);
388 }
389
390 rc = lstat(rpath, &mystat);
391 EUCA_FREE(rpath);
392 if ((rc < 0) || !S_ISBLK(mystat.st_mode)) {
393 LOGERROR("No stat information found for %s", rpath);
394 return (1);
395 }
396
397 return (0);
398 }
399
400 //!
401 //! Checks if a file is a readable regular file.
402 //!
403 //! @param[in] file
404 //!
405 //! @return 0 if the file is readable, 1 otherwise
406 //!
407 //! @pre The file field must not be NULL.
408 //!
409 int check_file(const char *file)
410 {
411 int rc = 0;
412 struct stat mystat = { 0 };
413
414 if (!file) {
415 return (1);
416 }
417
418 rc = lstat(file, &mystat);
419 if ((rc < 0) || !S_ISREG(mystat.st_mode)) {
420 return (1);
421 }
422
423 return (0);
424 }
425
426 //!
427 //! Check if a path exists
428 //!
429 //! @param[in] path
430 //!
431 //! @return 0 if the path exists, othewise 1 is returned.
432 //!
433 int check_path(const char *path)
434 {
435 int rc = 0;
436 struct stat mystat = { 0 };
437
438 if (!path) {
439 return (1);
440 }
441
442 if ((rc = lstat(path, &mystat)) < 0) {
443 return (1);
444 }
445
446 return (0);
447 }
448
449 //!
450 //! obtains size & available bytes of a file system that 'path' resides on
451 //! path may be a symbolic link, which will get resolved.
452 //!
453 //! @param[in] path
454 //! @param[out] fs_bytes_size
455 //! @param[out] fs_bytes_available
456 //! @param[out] fs_id
457 //!
458 //! @return EUCA_OK on success or the following error code.
459 //! \li EUCA_INVALID_ERROR
460 //! \li EUCA_IO_ERROR
461 //!
462 //! @pre \li The path, fs_bytes_size, fs_bytes_available and fs_id fields must not be NULL.
463 //! \li The path field must be a valid path.
464 //!
465 //! @post The fs_id, fs_bytes_size and fs_bytes_available fields are set appropriately.
466 //!
467 int statfs_path(const char *path, unsigned long long *fs_bytes_size, unsigned long long *fs_bytes_available, int *fs_id)
468 {
469 char cpath[PATH_MAX] = { 0 };
470 struct statfs fs = { 0 };
471
472 if ((path == NULL) || (fs_bytes_size == NULL) || (fs_bytes_available == NULL) || (fs_id == NULL))
473 return (EUCA_INVALID_ERROR);
474
475 errno = 0;
476
477 // will convert a path with symbolic links and '..' into canonical form
478 if (realpath(path, cpath) == NULL) {
479 LOGERROR("failed to resolve %s (%s)\n", path, strerror(errno));
480 return (EUCA_IO_ERROR);
481 }
482 // obtain the size and ID info from the file system of the canonical path
483 if (statfs(cpath, &fs) == -1) {
484 LOGERROR("failed to stat %s (%s)\n", cpath, strerror(errno));
485 return (EUCA_IO_ERROR);
486 }
487
488 *fs_id = hash_code_bin((char *)&fs.f_fsid, sizeof(fsid_t));
489 *fs_bytes_size = (long long)fs.f_bsize * (long long)(fs.f_blocks);
490 *fs_bytes_available = (long long)fs.f_bsize * (long long)(fs.f_bavail);
491
492 LOGDEBUG("path '%s' resolved\n", path);
493 LOGDEBUG(" to '%s' with ID %0x\n", cpath, *fs_id);
494 LOGDEBUG(" of size %llu bytes with available %llu bytes\n", *fs_bytes_size, *fs_bytes_available);
495
496 return (EUCA_OK);
497 }
498
499 //!
500 //!
501 //!
502 //! @param[in] fp
503 //!
504 //! @return a pointer to the file output string
505 //!
506 //! @pre \li The fp field MUST not be NULL.
507 //! \li The file handles must have previously been opened.
508 //!
509 //! @post The file remains open regardless of the result.
510 //!
511 //! @note caller is responsible to free the returned memory
512 //!
513 char *fp2str(FILE * fp)
514 {
515 #define INCREMENT 512
516
517 int buf_max = INCREMENT;
518 int buf_current = 0;
519 void *new_buf = NULL;
520 char *last_read = NULL;
521 char *buf = NULL;
522
523 if (fp == NULL)
524 return (NULL);
525
526 do {
527 // create/enlarge the buffer
528 if ((new_buf = EUCA_REALLOC(buf, buf_max, sizeof(char))) == NULL) {
529 // free partial buffer
530 EUCA_FREE(buf);
531 return (NULL);
532 }
533
534 memset((new_buf + buf_current), 0, (INCREMENT * sizeof(char)));
535
536 buf = new_buf;
537 LOGEXTREME("enlarged buf to %d\n", buf_max);
538
539 do { // read in until EOF or buffer is full
540 last_read = fgets(buf + buf_current, buf_max - buf_current, fp);
541 if (last_read != NULL) {
542 buf_current = strlen(buf);
543 } else if (!feof(fp)) {
544 LOGERROR("failed while reading from file handle\n");
545 EUCA_FREE(buf);
546 return (NULL);
547 }
548
549 LOGEXTREME("read %d characters so far (max=%d, last=%s)\n", buf_current, buf_max, last_read ? "no" : "yes");
550 } while (last_read && (buf_max > (buf_current + 1))); // +1 is needed for fgets() to put \0
551
552 // in case it is full
553 buf_max += INCREMENT;
554 } while (last_read);
555
556 return (buf);
557
558 #undef INCREMENT
559 }
560
561 //!
562 //! given a file path, prints it to stdout
563 //!
564 //! @param[in] file_name
565 //!
566 //! @return the number of bytes written to stdout
567 //!
568 int cat(const char *file_name)
569 {
570 int fd = 0;
571 int got = 0;
572 int put = 0;
573 char buf[BUFSIZE] = "";
574
575 if ((fd = open(file_name, O_RDONLY)) == -1) {
576 // we should print some error
577 return (put);
578 }
579
580 while ((got = read(fd, buf, BUFSIZE)) > 0) {
581 put += write(1, buf, got);
582 }
583
584 close(fd);
585 return (put);
586 }
587
588 //!
589 //! "touch" a file, creating if necessary
590 //!
591 //! @param[in] path
592 //!
593 //! @return EUCA_OK on success or EUCA_IO_ERROR on failure
594 //!
595 int touch(const char *path)
596 {
597 int ret = EUCA_OK;
598 int fd = -1;
599
600 if ((fd = open(path, O_WRONLY | O_CREAT | O_NONBLOCK, 0644)) >= 0) {
601 close(fd);
602 if (utime(path, NULL) != 0) {
603 LOGERROR("failed to adjust time for %s (%s)\n", path, strerror(errno));
604 ret = EUCA_IO_ERROR;
605 }
606 } else {
607 LOGERROR("failed to create/open file %s (%s)\n", path, strerror(errno));
608 ret = EUCA_IO_ERROR;
609 }
610 return (ret);
611 }
612
613 //!
614 //! diffs two files: 0=same, -N=different, N=error
615 //!
616 //! @param[in] path1
617 //! @param[in] path2
618 //!
619 //! @return 0=same, -N=different, N=error
620 //!
621 int diff(const char *path1, const char *path2)
622 {
623 int fd1 = 0;
624 int fd2 = 0;
625 int read1 = 0;
626 int read2 = 0;
627 char buf1[BUFSIZE] = "";
628 char buf2[BUFSIZE] = "";
629
630 if ((fd1 = open(path1, O_RDONLY)) < 0) {
631 LOGERROR("failed to open %s\n", path1);
632 } else if ((fd2 = open(path2, O_RDONLY)) < 0) {
633 LOGERROR("failed to open %s\n", path2);
634 close(fd1);
635 } else {
636 do {
637 read1 = read(fd1, buf1, BUFSIZE);
638 read2 = read(fd2, buf2, BUFSIZE);
639 if (read1 != read2)
640 break;
641
642 if (read1 && memcmp(buf1, buf2, read1))
643 break;
644 } while (read1);
645
646 close(fd1);
647 close(fd2);
648 return (-(read1 + read2)); // both should be 0s if files are equal
649 }
650 return EUCA_ERROR;
651 }
652
653 //!
654 //! sums up sizes of files in the directory, as well as the size of the
655 //! directory itself; no subdirectories are allowed - if there are any, this
656 //! returns -1
657 //!
658 //! @param[in] path
659 //!
660 //! @return the sommation of all file size in a directory
661 //!
662 long long dir_size(const char *path)
663 {
664 DIR *dir = NULL;
665 char *name = NULL;
666 char filepath[EUCA_MAX_PATH] = "";
667 unsigned char type = '\0';
668 long long size = 0;
669 struct stat mystat = { 0 };
670 struct dirent *dir_entry = NULL;
671
672 if ((dir = opendir(path)) == NULL) {
673 LOGWARN("unopeneable directory %s\n", path);
674 return (-1);
675 }
676
677 if (stat(path, &mystat) < 0) {
678 LOGWARN("could not stat %s\n", path);
679 closedir(dir);
680 return (-1);
681 }
682
683 size += ((long long)mystat.st_size);
684
685 while ((dir_entry = readdir(dir)) != NULL) {
686 name = dir_entry->d_name;
687 type = dir_entry->d_type;
688
689 if (!strcmp(".", name) || !strcmp("..", name))
690 continue;
691
692 if (DT_REG != type) {
693 LOGWARN("non-regular (type=%d) file %s/%s\n", type, path, name);
694 size = -1;
695 break;
696 }
697
698 snprintf(filepath, EUCA_MAX_PATH, "%s/%s", path, name);
699 if (stat(filepath, &mystat) < 0) {
700 LOGWARN("could not stat file %s\n", filepath);
701 size = -1;
702 break;
703 }
704
705 size += ((long long)mystat.st_size);
706 }
707
708 closedir(dir);
709 return (size);
710 }
711
712 //!
713 //!
714 //!
715 //! @param[in] path
716 //! @param[in] str
717 //!
718 //! @return EUCA_OK on success or EUCA_IO_ERROR on failure
719 //!
720 int write2file(const char *path, char *str)
721 {
722 FILE *FH = NULL;
723
724 if ((FH = fopen(path, "w")) != NULL) {
725 fprintf(FH, "%s", str);
726 fclose(FH);
727 return (EUCA_OK);
728 }
729
730 return (EUCA_IO_ERROR);
731 }
732
733 //!
734 //!
735 //!
736 //! @param[in] path
737 //! @param[in] limit
738 //!
739 //! @return the result of teh file2str() call
740 //!
741 //! @see file2str()
742 //!
743 //! @note the caller must free the memory when done.
744 //!
745 char *file2strn(const char *path, const ssize_t limit)
746 {
747 struct stat mystat = { 0 };
748
749 if (stat(path, &mystat) < 0) {
750 LOGERROR("could not stat file %s\n", path);
751 return (NULL);
752 }
753
754 if (mystat.st_size > limit) {
755 LOGERROR("file %s exceeds the limit (%lu) in file2strn()\n", path, limit);
756 return (NULL);
757 }
758
759 return (file2str(path));
760 }
761
762 //!
763 //! read file 'path' into a new string
764 //!
765 //! @param[in] path
766 //!
767 //! @return the string content of the given file
768 //!
769 //! @note the caller must free the memory when done.
770 //!
771 char *file2str(const char *path)
772 {
773 int fp = 0;
774 int bytes = 0;
775 int bytes_total = 0;
776 int to_read = 0;
777 char *p = NULL;
778 char *content = NULL;
779 off_t file_size = 0;
780 struct stat mystat = { 0 };
781
782 if (stat(path, &mystat) < 0) {
783 LOGERROR("errno: %d could not stat file %s\n", errno, path);
784 return (content);
785 }
786
787 file_size = mystat.st_size;
788
789 if ((content = EUCA_ALLOC((file_size + 1), sizeof(char))) == NULL) {
790 LOGERROR("out of memory reading file %s\n", path);
791 return (content);
792 }
793
794 if ((fp = open(path, O_RDONLY)) < 0) {
795 LOGERROR("errno: %d failed to open file %s\n", errno, path);
796 EUCA_FREE(content);
797 return (content);
798 }
799
800 p = content;
801 to_read = (((SSIZE_MAX) < file_size) ? (SSIZE_MAX) : file_size);
802 while ((bytes = read(fp, p, to_read)) > 0) {
803 bytes_total += bytes;
804 p += bytes;
805 if (to_read > (file_size - bytes_total)) {
806 to_read = file_size - bytes_total;
807 }
808 }
809
810 close(fp);
811
812 if (bytes < 0) {
813 LOGERROR("failed to read file %s\n", path);
814 EUCA_FREE(content);
815 return (content);
816 }
817
818 *p = '\0';
819 return (content);
820 }
821
822 //!
823 //!
824 //!
825 //! @param[in] file
826 //! @param[in] size
827 //! @param[in] mode
828 //!
829 //! @return the newly allocated string or NULL if any error occured
830 //!
831 //! @note caller is responsible to free the allocated memory
832 //!
833 char *file2str_seek(char *file, size_t size, int mode)
834 {
835 int rc = 0;
836 int fd = 0;
837 char *ret = NULL;
838 struct stat statbuf = { 0 };
839
840 if (!file || size <= 0) {
841 LOGERROR("bad input parameters\n");
842 return (NULL);
843 }
844
845 if ((ret = EUCA_ZALLOC(size, sizeof(char))) == NULL) {
846 LOGERROR("out of memory!\n");
847 return (NULL);
848 }
849
850 if ((rc = stat(file, &statbuf)) >= 0) {
851 if ((fd = open(file, O_RDONLY)) >= 0) {
852 if (mode == 1) {
853 if ((rc = lseek(fd, (off_t) (-1 * size), SEEK_END)) < 0) {
854 if ((rc = lseek(fd, (off_t) 0, SEEK_SET)) < 0) {
855 LOGERROR("cannot seek\n");
856 EUCA_FREE(ret);
857 close(fd);
858 return (NULL);
859 }
860 }
861 }
862
863 rc = read(fd, ret, (size) - 1);
864 close(fd);
865 } else {
866 LOGERROR("cannot open '%s' read-only\n", file);
867 EUCA_FREE(ret);
868 return (NULL);
869 }
870 } else {
871 LOGERROR("cannot stat console_output file '%s'\n", file);
872 EUCA_FREE(ret);
873 return (NULL);
874 }
875
876 return (ret);
877 }
878
879 //!
880 //! Write a NULL-terminated string to a file according to a file
881 //! specification. If 'mktemp' is TRUE, 'path' is expected to be
882 //! not a file path, but a template for a temporary file name,
883 //! according to the mkstemp() specification, with six X's in it.
884 //! Otherwise, 'path' is the path of the file to create and write
885 //! the string to.
886 //!
887 //! @param[in] str String to write to a file.
888 //! @param[in] path Path of the file to create or mktemp spec.
889 //! @param[in] flags Same flags as accepted by open() call. Ignored when mktemp is TRUE.
890 //! @param[in] mode Permissions of the file to create.
891 //! @param[in] mktemp Flag requesting a temporary file.
892 //!
893 //! @return EUCA_OK on success and -1 on failure.
894 //!
895 int str2file(const char *str, char *path, int flags, mode_t mode, boolean mktemp)
896 {
897 if (path == NULL)
898 return 1;
899
900 int fd;
901
902 // if temporary file was requested, assume that the path is actually
903 // a template for mkstemp(), with 6 X's in it, and that it is to be
904 // overwritten with the actual file path
905 if (mktemp) {
906 fd = safe_mkstemp(path);
907 if (fd < 0) {
908 LOGERROR("cannot create temporary file '%s': %s\n", path, strerror(errno));
909 return (-1);
910 }
911 if (fchmod(fd, mode)) {
912 LOGERROR("failed to change permissions on '%s': %s\n", path, strerror(errno));
913 close(fd);
914 return (-1);
915 }
916 } else {
917 fd = open(path, flags, mode);
918 if (fd == -1) {
919 LOGERROR("failed to create file '%s': %s\n", path, strerror(errno));
920 return (-1);
921 }
922 }
923
924 if (str) {
925 int to_write = strlen(str);
926 int offset = 0;
927 while (to_write > 0) {
928 int wrote = write(fd, str + offset, to_write);
929 if (wrote == -1) {
930 LOGERROR("failed to write to file '%s': %s\n", path, strerror(errno));
931 close(fd);
932 return (-1);
933 }
934 to_write -= wrote;
935 offset += wrote;
936 }
937 }
938 close(fd);
939
940 return (EUCA_OK);
941 }
942
943 //!
944 //! copies contents of src to dst, possibly overwriting whatever is in dst
945 //!
946 //! @param[in] src
947 //! @param[in] dst
948 //!
949 //! @return EUCA_OK on success or EUCA_IO_ERROR on failure
950 //!
951 int copy_file(const char *src, const char *dst)
952 {
953 #define _BUFSIZE 16384
954
955 int ret = EUCA_OK;
956 int ifp = 0;
957 int ofp = 0;
958 char buf[_BUFSIZE] = "";
959 ssize_t bytes = 0;
960 struct stat mystat = { 0 };
961
962 if (stat(src, &mystat) < 0) {
963 LOGERROR("cannot stat '%s'\n", src);
964 return (EUCA_IO_ERROR);
965 }
966
967 if ((ifp = open(src, O_RDONLY)) < 0) {
968 LOGERROR("failed to open the input file '%s'\n", src);
969 return (EUCA_IO_ERROR);
970 }
971
972 if ((ofp = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) {
973 LOGERROR("failed to create the ouput file '%s'\n", dst);
974 close(ifp);
975 return (EUCA_IO_ERROR);
976 }
977
978 while ((bytes = read(ifp, buf, _BUFSIZE)) > 0) {
979 if (write(ofp, buf, bytes) < 1) {
980 LOGERROR("failed while writing to '%s'\n", dst);
981 ret = EUCA_IO_ERROR;
982 break;
983 }
984 }
985
986 if (bytes < 0) {
987 LOGERROR("failed while writing to '%s'\n", dst);
988 ret = EUCA_IO_ERROR;
989 }
990
991 close(ifp);
992 close(ofp);
993
994 return (ret);
995
996 #undef _BUFSIZE
997 }
998
999 //!
1000 //!
1001 //!
1002 //! @param[in] file_path
1003 //!
1004 //! @return the size of the file
1005 //!
1006 long long file_size(const char *file_path)
1007 {
1008 int err = 0;
1009 struct stat mystat = { 0 };
1010
1011 if ((err = stat(file_path, &mystat)) < 0)
1012 return ((long long)err);
1013 return ((long long)mystat.st_size);
1014 }
1015
1016 //! Removes duplicate '/' in paths in-place.
1017 //!
1018 //! @param[in] path to modify in-place
1019 //!
1020 void dedup_path(char *path)
1021 {
1022 int i = 0;
1023 int j = 0;
1024 int src_len;
1025 int duplicate_detected = 0;
1026
1027 if (path == NULL) {
1028 return;
1029 }
1030
1031 src_len = strlen(path);
1032 if (src_len <= 1) {
1033 return;
1034 }
1035 //Include copying the null terminator
1036 for (i = 1, j = 0; i < src_len + 1; i++) {
1037 if (duplicate_detected && path[i] != '/') {
1038 duplicate_detected = 0;
1039 } else if (path[j] == '/' && path[i] == '/') {
1040 duplicate_detected = 1;
1041 }
1042
1043 if (!duplicate_detected) {
1044 path[++j] = path[i];
1045 }
1046 }
1047 return;
1048 }
1049
1050 //!
1051 //! given path=A/B/C and only A existing, create A/B and, unless
1052 //! is_file_path==1, also create A/B/C directory
1053 //!
1054 //! @param[in] path
1055 //! @param[in] is_file_path
1056 //! @param[in] user
1057 //! @param[in] group
1058 //! @param[in] mode
1059 //!
1060 //! @return 0 = path already existed, 1 = created OK, -1 = error
1061 //!
1062 int ensure_directories_exist(const char *path, int is_file_path, const char *user, const char *group, mode_t mode)
1063 {
1064 int ret = 0;
1065 int i = 0;
1066 int len = strlen(path);
1067 int try_dir = 0;
1068 char *path_copy = NULL;
1069 struct stat buf = { 0 };
1070
1071 if (len > 0)
1072 path_copy = strdup(path);
1073
1074 if (path_copy == NULL)
1075 return (-1);
1076
1077 for (i = 0; i < len; i++) {
1078 try_dir = 0;
1079
1080 if ((path[i] == '/') && (i > 0)) {
1081 // dir path, not root
1082 path_copy[i] = '\0';
1083 try_dir = 1;
1084
1085 } else if ((path[i] != '/') && ((i + 1) == len)) {
1086 // last one
1087 if (!is_file_path)
1088 try_dir = 1;
1089 }
1090
1091 if (try_dir) {
1092 if (stat(path_copy, &buf) == -1) {
1093 LOGINFO("creating path %s\n", path_copy);
1094
1095 if (mkdir(path_copy, mode) == -1) {
1096 LOGERROR("failed to create path %s: %s\n", path_copy, strerror(errno));
1097
1098 EUCA_FREE(path_copy);
1099 return (-1);
1100 }
1101
1102 ret = 1; // we created a directory
1103
1104 if (diskutil_ch(path_copy, user, group, mode) != EUCA_OK) {
1105 LOGERROR("failed to change perms on path %s\n", path_copy);
1106 EUCA_FREE(path_copy);
1107 return (-1);
1108 }
1109 }
1110
1111 path_copy[i] = '/'; // restore the slash
1112 }
1113 }
1114
1115 EUCA_FREE(path_copy);
1116 return (ret);
1117 }
1118
1119 //!
1120 //! ensure the temp file is only readable by the user
1121 //!
1122 //! @param[in] template
1123 //!
1124 //! @return -1 on failure or the corresponding files descriptor for the temp file
1125 //!
1126 int safe_mkstemp(char *template)
1127 {
1128 int ret = 0;
1129 ret = mkstemp(template);
1130 return (ret);
1131 }