Line data Source code
1 : //! ***********************************************************************
2 : //!
3 : //! Copyright (C) 2018 Robert Farmer & The MESA Team
4 : //!
5 : //! This program is free software: you can redistribute it and/or modify
6 : //! it under the terms of the GNU Lesser General Public License
7 : //! as published by the Free Software Foundation,
8 : //! either version 3 of the License, or (at your option) any later version.
9 : //!
10 : //! This program is distributed in the hope that it will be useful,
11 : //! but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : //! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 : //! See the GNU Lesser General Public License for more details.
14 : //!
15 : //! You should have received a copy of the GNU Lesser General Public License
16 : //! along with this program. If not, see <https://www.gnu.org/licenses/>.
17 : //!
18 : //! ***********************************************************************
19 :
20 : #include "utils_c_system.h"
21 : #include <dirent.h>
22 : #include <errno.h>
23 : #include <fcntl.h>
24 : #include <stdbool.h>
25 : #include <stdio.h>
26 : #include <stdlib.h>
27 : #include <string.h>
28 : #include <sys/stat.h> /* mkdir(2) */
29 : #include <sys/types.h>
30 : #include <unistd.h>
31 :
32 : #define PATHLEN 4096
33 : static const int SUCCESS = 0;
34 : static const char TEMPLATE[] = ".temp-XXXXXX";
35 :
36 : /* Makes a single directory at path (mkdir path) */
37 64 : int c_mkdir(const char *restrict path) {
38 :
39 : // If folder already exists return, don't relay on EEXIST as sometimes
40 : // EACCES happens first
41 64 : if (is_dir(path) == 1) {
42 : return SUCCESS;
43 : }
44 :
45 27 : if (mkdir(path, S_IRWXU) != SUCCESS) {
46 0 : if (errno != EEXIST) {
47 0 : printf("MKDIR error on '%s' Errno %d :: %s\n", path, errno,
48 : strerror(errno));
49 0 : return -1;
50 : }
51 : }
52 : return SUCCESS;
53 : }
54 :
55 : /* Makes a directory at path, potentially making needed parent directories
56 : * (mkdir -p path) */
57 30 : int c_mkdir_p(const char *restrict path) {
58 30 : char _path[PATHLEN];
59 30 : char *p;
60 :
61 : // If folder already exists return, don't relay on EEXIST as sometimes
62 : // EACCES happens first
63 30 : if (is_dir(path) == 1) {
64 : return SUCCESS;
65 : }
66 :
67 20 : if (strlen(path) >= PATHLEN) {
68 : return -1;
69 : }
70 :
71 20 : strcpy(_path, path);
72 :
73 : /* Iterate the string */
74 542 : for (p = _path + 1; *p; p++) {
75 522 : if (*p == '/') {
76 : /* Temporarily truncate */
77 44 : *p = '\0';
78 :
79 44 : if (c_mkdir(_path) != SUCCESS) {
80 : return -1;
81 : }
82 :
83 44 : *p = '/';
84 : }
85 : }
86 20 : if (c_mkdir(_path) != SUCCESS) {
87 : return -1;
88 : }
89 :
90 : return SUCCESS;
91 : }
92 :
93 102 : int is_dir(const char *restrict path) {
94 102 : DIR *dir = opendir(path);
95 102 : if (dir) {
96 53 : closedir(dir);
97 53 : return 1;
98 : } else {
99 : return 0;
100 : }
101 : }
102 :
103 : /* Moves file src to dest (mv src dest)
104 : * if src and dest are on the same filesystem, then mv the files
105 : * if not, copy src to a temp file on same filesystem as dest then mv the temp
106 : * file to dest, this prevents the filesystem ever being in a inconsistent
107 : * state. dest either does not exist or does exist and is "full", thus we
108 : * prevent problems with other programs reading the file before its finished
109 : * being written.
110 : */
111 :
112 213 : int c_mv(const char *restrict src, const char *restrict dest) {
113 213 : char dest_temp[PATHLEN];
114 :
115 213 : if (rename(src, dest) == SUCCESS) {
116 : return SUCCESS;
117 : }
118 :
119 0 : if (errno != EXDEV) {
120 : return -1;
121 : }
122 :
123 0 : if (strlen(dest) >= PATHLEN) {
124 : return -1;
125 : }
126 :
127 0 : strcpy(dest_temp, dest);
128 :
129 0 : size_t i = strlen(dest);
130 0 : while (i) {
131 0 : if (dest_temp[i - 1] == '/') {
132 : break;
133 : }
134 :
135 : i--;
136 : }
137 :
138 0 : if (strlen(TEMPLATE) >= PATHLEN - i) {
139 : return -1;
140 : }
141 :
142 0 : strcpy(dest_temp + i, TEMPLATE);
143 :
144 0 : int fd = mkstemp(dest_temp);
145 :
146 0 : if (fd == -1) {
147 : return -1;
148 : }
149 :
150 0 : close(fd);
151 :
152 0 : if (c_cp(src, dest_temp) != SUCCESS) {
153 0 : remove(dest_temp);
154 0 : return -1;
155 : }
156 :
157 0 : if (rename(dest_temp, dest) != SUCCESS) {
158 0 : remove(dest_temp);
159 0 : return -1;
160 : }
161 :
162 0 : remove(src);
163 :
164 0 : return SUCCESS;
165 : }
166 :
167 : /* copies file src to dest (cp src dest) */
168 0 : int c_cp(const char *restrict src, const char *restrict dest) {
169 0 : const int BUF_SIZE = 4096;
170 0 : int fd_src, fd_dest;
171 0 : char buf[BUF_SIZE];
172 0 : ssize_t numRead;
173 0 : const int dbg = false;
174 :
175 0 : if (dbg) {
176 : printf("cp %s to %s\n", src, dest);
177 : }
178 :
179 0 : fd_src = open(src, O_RDONLY);
180 0 : if (fd_src < 0) {
181 : return -1;
182 : }
183 :
184 0 : fd_dest = open(dest, O_CREAT | O_WRONLY | O_TRUNC,
185 : S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
186 0 : if (fd_dest < 0) {
187 0 : goto error;
188 : }
189 :
190 0 : while ((numRead = read(fd_src, buf, BUF_SIZE)) > 0) {
191 0 : if (write(fd_dest, buf, numRead) != numRead) {
192 0 : goto error;
193 : }
194 : }
195 :
196 0 : if (numRead == -1) {
197 0 : goto error;
198 : }
199 :
200 0 : close(fd_src);
201 0 : close(fd_dest);
202 :
203 0 : return SUCCESS;
204 :
205 0 : error:
206 0 : close(fd_src);
207 0 : if (fd_dest >= 0) {
208 0 : close(fd_dest);
209 : }
210 : return -2;
211 : }
|