1 /*
2 * Directory and file system operations
3 *
4 * Copyright (C) 2013 Craig Barratt.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
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 along
17 * with this program; if not, visit the http://fsf.org website.
18 */
19
20 #include "backuppc.h"
21
22 /*
23 * Create all the directories in the given path. Path must be non-const. Trailing '/' characters are removed.
24 */
bpc_path_create(char * path)25 int bpc_path_create(char *path)
26 {
27 char *p = path;
28 STRUCT_STAT st;
29 int levels = 0;
30
31 if ( BPC_LogLevel >= 6 ) bpc_logMsgf("bpc_path_create(%s)\n", path);
32 /*
33 * check if it exists already
34 */
35 if ( !stat(path, &st) && S_ISDIR(st.st_mode) ) return 0;
36
37 /*
38 * We walk up until we find the deepest level directory that exists.
39 * First remove trailing slashes.
40 */
41 p = path + strlen(path);
42 while ( p > path && p[-1] == '/' ) p--;
43 if ( *p == '/' ) *p = '\0';
44 while ( p > path ) {
45 while ( p > path && p[-1] != '/' ) p--;
46 while ( p > path && p[-1] == '/' ) p--;
47 if ( *p == '/' ) {
48 *p = '\0';
49 levels++;
50 if ( !stat(path, &st) && S_ISDIR(st.st_mode) ) break;
51 }
52 }
53 if ( BPC_LogLevel >= 9 ) bpc_logMsgf("bpc_path_create: found that %s exists (%d levels up)\n", path, levels);
54
55 /*
56 * We have removed levels '/' characters from path. Replace each one and create the directory.
57 */
58 while ( levels-- > 0 ) {
59 p = path + strlen(path);
60 *p = '/';
61 if ( mkdir(path, ACCESSPERMS) < 0 && errno != EEXIST) {
62 bpc_logErrf("bpc_path_create: can't create %s (errno %d)\n", path, errno);
63 return -1;
64 }
65 if ( BPC_LogLevel >= 9 ) bpc_logMsgf("bpc_path_create: created %s\n", path);
66 }
67 return 0;
68 }
69
70 /*
71 * Remove all the files below path (if a directory) and path itself. Deduct reference counts
72 * for every attrib file removed.
73 *
74 * Note that inodes are *not* updated, even in cases where nlinks > 0.
75 */
bpc_path_remove(bpc_deltaCount_info * deltaInfo,char * path,int compress)76 int bpc_path_remove(bpc_deltaCount_info *deltaInfo, char *path, int compress)
77 {
78 char filePath[BPC_MAXPATHLEN];
79 STRUCT_STAT st;
80 DIR *dir;
81 struct dirent *dp;
82 int errorCnt = 0;
83 size_t dirListSize = 0, dirListLen = 0;
84 char *dirList = NULL, *dirListP;
85
86 if ( BPC_LogLevel >= 6 ) bpc_logMsgf("bpc_path_remove(%s)\n", path);
87 if ( !(dir = opendir(path)) ) {
88 unlink(path);
89 return errorCnt;
90 }
91 while ( (dp = readdir(dir)) ) {
92 if ( !strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") ) continue;
93 snprintf(filePath, sizeof(filePath), "%s/%s", path, dp->d_name);
94 if ( BPC_LogLevel >= 8 ) bpc_logMsgf("bpc_path_remove: removing %s\n", filePath);
95 if ( stat(filePath, &st) ) {
96 /*
97 * hmmm. stat failed - just try to remove it
98 */
99 unlink(filePath);
100 continue;
101 }
102 if ( S_ISDIR(st.st_mode) ) {
103 /*
104 * To avoid recursing with dir still open (consuming an open fd), remember all the dirs
105 * and recurse after we close dir.
106 */
107 if ( !dirList ) {
108 dirListSize = 4096;
109 if ( !(dirList = malloc(dirListSize)) ) {
110 bpc_logErrf("bpc_path_remove: can't allocate %u bytes\n", (unsigned)dirListSize);
111 return ++errorCnt;
112 }
113 }
114 if ( dirListLen + strlen(dp->d_name) + 1 >= dirListSize ) {
115 dirListSize = dirListSize * 2 + strlen(dp->d_name);
116 if ( !(dirList = realloc(dirList, dirListSize)) ) {
117 bpc_logErrf("bpc_path_remove: can't reallocate %u bytes\n", (unsigned)dirListSize);
118 return ++errorCnt;
119 }
120 }
121 strcpy(dirList + dirListLen, dp->d_name);
122 dirListLen += strlen(dp->d_name) + 1;
123 } else {
124 /*
125 * if this is an attrib file, we need to read it and deduct the reference counts.
126 */
127 if ( !strncmp(dp->d_name, "attrib", 6) ) {
128 bpc_attrib_dir dir;
129
130 bpc_attrib_dirInit(&dir, compress);
131 if ( bpc_attrib_dirRead(&dir, NULL, filePath, 0) ) {
132 bpc_logErrf("bpc_path_remove: can't read attrib file %s\n", filePath);
133 errorCnt++;
134 }
135 if ( BPC_LogLevel >= 9 ) bpc_logMsgf("bpc_path_remove: adjusting ref counts from attrib file %s\n", filePath);
136 if ( !unlink(filePath) ) {
137 /*
138 * Only reduce the ref counts if we succeeded in removing the attrib file
139 */
140 bpc_attrib_dirRefCount(deltaInfo, &dir, -1);
141 }
142 bpc_attrib_dirDestroy(&dir);
143 } else {
144 if ( unlink(filePath) ) errorCnt++;
145 }
146 }
147 }
148 closedir(dir);
149 /*
150 * Now visit the subdirs we have saved above.
151 */
152 if ( dirList ) {
153 for ( dirListP = dirList ; dirListP < dirList + dirListLen ; dirListP += strlen(dirListP) + 1 ) {
154 snprintf(filePath, sizeof(filePath), "%s/%s", path, dirListP);
155 errorCnt += bpc_path_remove(deltaInfo, filePath, compress);
156 }
157 free(dirList);
158 }
159 if ( rmdir(path) ) errorCnt++;
160 return errorCnt;
161 }
162
163 /*
164 * Reference count all the files below the directory path, based on the attrib
165 * files in and below path.
166 */
bpc_path_refCountAllInodeMax(bpc_deltaCount_info * deltaInfo,char * path,int compress,int incr,unsigned int * inodeMax)167 int bpc_path_refCountAllInodeMax(bpc_deltaCount_info *deltaInfo, char *path, int compress, int incr, unsigned int *inodeMax)
168 {
169 char filePath[BPC_MAXPATHLEN];
170 STRUCT_STAT st;
171 DIR *dir;
172 struct dirent *dp;
173 int errorCnt = 0;
174 size_t dirListSize = 0, dirListLen = 0;
175 char *dirList = NULL, *dirListP;
176
177 if ( BPC_LogLevel >= 6 ) bpc_logMsgf("bpc_path_refCountAll(%s)\n", path);
178 if ( !(dir = opendir(path)) ) {
179 return errorCnt;
180 }
181 while ( (dp = readdir(dir)) ) {
182 if ( !strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") ) continue;
183 snprintf(filePath, sizeof(filePath), "%s/%s", path, dp->d_name);
184 if ( BPC_LogLevel >= 8 ) bpc_logMsgf("bpc_path_refCountAll: got %s\n", filePath);
185 if ( stat(filePath, &st) ) continue;
186 if ( S_ISDIR(st.st_mode) ) {
187 /*
188 * To avoid recursing with dir still open (consuming an open fd), remember all the dirs
189 * and recurse after we close dir.
190 */
191 if ( !dirList ) {
192 dirListSize = 4096;
193 if ( !(dirList = malloc(dirListSize)) ) {
194 bpc_logErrf("bpc_path_refCountAll: can't allocate %u bytes\n", (unsigned)dirListSize);
195 return ++errorCnt;
196 }
197 }
198 if ( dirListLen + strlen(dp->d_name) + 1 >= dirListSize ) {
199 dirListSize = dirListSize * 2 + strlen(dp->d_name);
200 if ( !(dirList = realloc(dirList, dirListSize)) ) {
201 bpc_logErrf("bpc_path_refCountAll: can't reallocate %u bytes\n", (unsigned)dirListSize);
202 return ++errorCnt;
203 }
204 }
205 strcpy(dirList + dirListLen, dp->d_name);
206 dirListLen += strlen(dp->d_name) + 1;
207 } else {
208 /*
209 * if this is an attrib file, we need to read it and deduct the reference counts.
210 */
211 if ( !strncmp(dp->d_name, "attrib", 6) ) {
212 bpc_attrib_dir dir;
213
214 bpc_attrib_dirInit(&dir, compress);
215 if ( bpc_attrib_dirRead(&dir, path, dp->d_name, 0) ) {
216 bpc_logErrf("bpc_path_refCountAll: can't read attrib file %s\n", filePath);
217 errorCnt++;
218 } else {
219 if ( BPC_LogLevel >= 9 ) bpc_logMsgf("bpc_path_refCountAll: adjusting ref counts from attrib file %s\n", filePath);
220 bpc_attrib_dirRefCountInodeMax(deltaInfo, &dir, incr, inodeMax);
221 }
222 bpc_attrib_dirDestroy(&dir);
223 }
224 }
225 }
226 closedir(dir);
227 /*
228 * Now visit the subdirs we have saved above.
229 */
230 if ( dirList ) {
231 for ( dirListP = dirList ; dirListP < dirList + dirListLen ; dirListP += strlen(dirListP) + 1 ) {
232 snprintf(filePath, sizeof(filePath), "%s/%s", path, dirListP);
233 errorCnt += bpc_path_refCountAllInodeMax(deltaInfo, filePath, compress, incr, inodeMax);
234 }
235 free(dirList);
236 }
237 return errorCnt;
238 }
239
240 /*
241 * Reference count all the files below the directory path, based on the attrib
242 * files in and below path.
243 */
bpc_path_refCountAll(bpc_deltaCount_info * deltaInfo,char * path,int compress,int incr)244 int bpc_path_refCountAll(bpc_deltaCount_info *deltaInfo, char *path, int compress, int incr)
245 {
246 return bpc_path_refCountAllInodeMax(deltaInfo, path, compress, incr, NULL);
247 }
248
249 /*
250 * Add an exclusive lock to the byte range in the given file.
251 * Blocks until the lock becomes available.
252 */
bpc_lockRangeFd(int fd,OFF_T offset,OFF_T len,int block)253 int bpc_lockRangeFd(int fd, OFF_T offset, OFF_T len, int block)
254 {
255 struct flock lock;
256
257 lock.l_type = F_WRLCK;
258 lock.l_whence = SEEK_SET;
259 lock.l_start = offset;
260 lock.l_len = len;
261 lock.l_pid = 0;
262
263 return fcntl(fd, block ? F_SETLKW : F_SETLK, &lock);
264 }
265
bpc_unlockRangeFd(int fd,OFF_T offset,OFF_T len)266 int bpc_unlockRangeFd(int fd, OFF_T offset, OFF_T len)
267 {
268 struct flock lock;
269
270 lock.l_type = F_UNLCK;
271 lock.l_whence = SEEK_SET;
272 lock.l_start = offset;
273 lock.l_len = len;
274 lock.l_pid = 0;
275
276 return fcntl(fd, F_SETLK, &lock);
277 }
278
bpc_lockRangeFile(char * lockFile,OFF_T offset,OFF_T len,int block)279 int bpc_lockRangeFile(char *lockFile, OFF_T offset, OFF_T len, int block)
280 {
281 int fd;
282
283 if ( (fd = open(lockFile, O_CREAT | O_RDWR, 0660)) < 0 ) {
284 bpc_logErrf("bpc_lockRangeFile: can't open/create lock file %s\n", lockFile);
285 return fd;
286 }
287 if ( bpc_lockRangeFd(fd, offset, len, block) ) {
288 close(fd);
289 if ( block ) {
290 bpc_logErrf("bpc_lockRangeFile: lock(%s) failed (errno = %d)\n", lockFile, errno);
291 }
292 return -1;
293 }
294 return fd;
295 }
296
bpc_unlockRangeFile(int lockFd)297 void bpc_unlockRangeFile(int lockFd)
298 {
299 if ( lockFd >= 0 ) close(lockFd);
300 }
301