File Coverage

bpc_dirOps.c
Criterion Covered Total %
statement 0 139 0.0
branch 0 122 0.0
condition n/a
subroutine n/a
pod n/a
total 0 261 0.0


line stmt bran cond sub pod time code
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             */
25 0           int bpc_path_create(char *path)
26             {
27 0           char *p = path;
28             STRUCT_STAT st;
29 0           int levels = 0;
30              
31 0 0         if ( BPC_LogLevel >= 6 ) bpc_logMsgf("bpc_path_create(%s)\n", path);
32             /*
33             * check if it exists already
34             */
35 0 0         if ( !stat(path, &st) && S_ISDIR(st.st_mode) ) return 0;
    0          
36              
37             /*
38             * We walk up until we find the deepest level directory that exists.
39             * First remove trailing slashes.
40             */
41 0           p = path + strlen(path);
42 0 0         while ( p > path && p[-1] == '/' ) p--;
    0          
43 0 0         if ( *p == '/' ) *p = '\0';
44 0 0         while ( p > path ) {
45 0 0         while ( p > path && p[-1] != '/' ) p--;
    0          
46 0 0         while ( p > path && p[-1] == '/' ) p--;
    0          
47 0 0         if ( *p == '/' ) {
48 0           *p = '\0';
49 0           levels++;
50 0 0         if ( !stat(path, &st) && S_ISDIR(st.st_mode) ) break;
    0          
51             }
52             }
53 0 0         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 0 0         while ( levels-- > 0 ) {
59 0           p = path + strlen(path);
60 0           *p = '/';
61 0 0         if ( mkdir(path, ACCESSPERMS) < 0 && errno != EEXIST) {
    0          
62 0           bpc_logErrf("bpc_path_create: can't create %s (errno %d)\n", path, errno);
63 0           return -1;
64             }
65 0 0         if ( BPC_LogLevel >= 9 ) bpc_logMsgf("bpc_path_create: created %s\n", path);
66             }
67 0           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             */
76 0           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 0           int errorCnt = 0;
83 0           size_t dirListSize = 0, dirListLen = 0;
84 0           char *dirList = NULL, *dirListP;
85              
86 0 0         if ( BPC_LogLevel >= 6 ) bpc_logMsgf("bpc_path_remove(%s)\n", path);
87 0 0         if ( !(dir = opendir(path)) ) {
88 0           unlink(path);
89 0           return errorCnt;
90             }
91 0 0         while ( (dp = readdir(dir)) ) {
92 0 0         if ( !strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") ) continue;
    0          
93 0           snprintf(filePath, sizeof(filePath), "%s/%s", path, dp->d_name);
94 0 0         if ( BPC_LogLevel >= 8 ) bpc_logMsgf("bpc_path_remove: removing %s\n", filePath);
95 0 0         if ( stat(filePath, &st) ) {
96             /*
97             * hmmm. stat failed - just try to remove it
98             */
99 0           unlink(filePath);
100 0           continue;
101             }
102 0 0         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 0 0         if ( !dirList ) {
108 0           dirListSize = 4096;
109 0 0         if ( !(dirList = malloc(dirListSize)) ) {
110 0           bpc_logErrf("bpc_path_remove: can't allocate %u bytes\n", (unsigned)dirListSize);
111 0           return ++errorCnt;
112             }
113             }
114 0 0         if ( dirListLen + strlen(dp->d_name) + 1 >= dirListSize ) {
115 0           dirListSize = dirListSize * 2 + strlen(dp->d_name);
116 0 0         if ( !(dirList = realloc(dirList, dirListSize)) ) {
117 0           bpc_logErrf("bpc_path_remove: can't reallocate %u bytes\n", (unsigned)dirListSize);
118 0           return ++errorCnt;
119             }
120             }
121 0           strcpy(dirList + dirListLen, dp->d_name);
122 0           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 0 0         if ( !strncmp(dp->d_name, "attrib", 6) ) {
128             bpc_attrib_dir dir;
129              
130 0           bpc_attrib_dirInit(&dir, compress);
131 0 0         if ( bpc_attrib_dirRead(&dir, NULL, filePath, 0) ) {
132 0           bpc_logErrf("bpc_path_remove: can't read attrib file %s\n", filePath);
133 0           errorCnt++;
134             }
135 0 0         if ( BPC_LogLevel >= 9 ) bpc_logMsgf("bpc_path_remove: adjusting ref counts from attrib file %s\n", filePath);
136 0 0         if ( !unlink(filePath) ) {
137             /*
138             * Only reduce the ref counts if we succeeded in removing the attrib file
139             */
140 0           bpc_attrib_dirRefCount(deltaInfo, &dir, -1);
141             }
142 0           bpc_attrib_dirDestroy(&dir);
143             } else {
144 0 0         if ( unlink(filePath) ) errorCnt++;
145             }
146             }
147             }
148 0           closedir(dir);
149             /*
150             * Now visit the subdirs we have saved above.
151             */
152 0 0         if ( dirList ) {
153 0 0         for ( dirListP = dirList ; dirListP < dirList + dirListLen ; dirListP += strlen(dirListP) + 1 ) {
154 0           snprintf(filePath, sizeof(filePath), "%s/%s", path, dirListP);
155 0           errorCnt += bpc_path_remove(deltaInfo, filePath, compress);
156             }
157 0           free(dirList);
158             }
159 0 0         if ( rmdir(path) ) errorCnt++;
160 0           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             */
167 0           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 0           int errorCnt = 0;
174 0           size_t dirListSize = 0, dirListLen = 0;
175 0           char *dirList = NULL, *dirListP;
176              
177 0 0         if ( BPC_LogLevel >= 6 ) bpc_logMsgf("bpc_path_refCountAll(%s)\n", path);
178 0 0         if ( !(dir = opendir(path)) ) {
179 0           return errorCnt;
180             }
181 0 0         while ( (dp = readdir(dir)) ) {
182 0 0         if ( !strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") ) continue;
    0          
183 0           snprintf(filePath, sizeof(filePath), "%s/%s", path, dp->d_name);
184 0 0         if ( BPC_LogLevel >= 8 ) bpc_logMsgf("bpc_path_refCountAll: got %s\n", filePath);
185 0 0         if ( stat(filePath, &st) ) continue;
186 0 0         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 0 0         if ( !dirList ) {
192 0           dirListSize = 4096;
193 0 0         if ( !(dirList = malloc(dirListSize)) ) {
194 0           bpc_logErrf("bpc_path_refCountAll: can't allocate %u bytes\n", (unsigned)dirListSize);
195 0           return ++errorCnt;
196             }
197             }
198 0 0         if ( dirListLen + strlen(dp->d_name) + 1 >= dirListSize ) {
199 0           dirListSize = dirListSize * 2 + strlen(dp->d_name);
200 0 0         if ( !(dirList = realloc(dirList, dirListSize)) ) {
201 0           bpc_logErrf("bpc_path_refCountAll: can't reallocate %u bytes\n", (unsigned)dirListSize);
202 0           return ++errorCnt;
203             }
204             }
205 0           strcpy(dirList + dirListLen, dp->d_name);
206 0           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 0 0         if ( !strncmp(dp->d_name, "attrib", 6) ) {
212             bpc_attrib_dir dir;
213              
214 0           bpc_attrib_dirInit(&dir, compress);
215 0 0         if ( bpc_attrib_dirRead(&dir, path, dp->d_name, 0) ) {
216 0           bpc_logErrf("bpc_path_refCountAll: can't read attrib file %s\n", filePath);
217 0           errorCnt++;
218             } else {
219 0 0         if ( BPC_LogLevel >= 9 ) bpc_logMsgf("bpc_path_refCountAll: adjusting ref counts from attrib file %s\n", filePath);
220 0           bpc_attrib_dirRefCountInodeMax(deltaInfo, &dir, incr, inodeMax);
221             }
222 0           bpc_attrib_dirDestroy(&dir);
223             }
224             }
225             }
226 0           closedir(dir);
227             /*
228             * Now visit the subdirs we have saved above.
229             */
230 0 0         if ( dirList ) {
231 0 0         for ( dirListP = dirList ; dirListP < dirList + dirListLen ; dirListP += strlen(dirListP) + 1 ) {
232 0           snprintf(filePath, sizeof(filePath), "%s/%s", path, dirListP);
233 0           errorCnt += bpc_path_refCountAllInodeMax(deltaInfo, filePath, compress, incr, inodeMax);
234             }
235 0           free(dirList);
236             }
237 0           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             */
244 0           int bpc_path_refCountAll(bpc_deltaCount_info *deltaInfo, char *path, int compress, int incr)
245             {
246 0           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             */
253 0           int bpc_lockRangeFd(int fd, OFF_T offset, OFF_T len, int block)
254             {
255             struct flock lock;
256              
257 0           lock.l_type = F_WRLCK;
258 0           lock.l_whence = SEEK_SET;
259 0           lock.l_start = offset;
260 0           lock.l_len = len;
261 0           lock.l_pid = 0;
262              
263 0 0         return fcntl(fd, block ? F_SETLKW : F_SETLK, &lock);
264             }
265              
266 0           int bpc_unlockRangeFd(int fd, OFF_T offset, OFF_T len)
267             {
268             struct flock lock;
269              
270 0           lock.l_type = F_UNLCK;
271 0           lock.l_whence = SEEK_SET;
272 0           lock.l_start = offset;
273 0           lock.l_len = len;
274 0           lock.l_pid = 0;
275              
276 0           return fcntl(fd, F_SETLK, &lock);
277             }
278              
279 0           int bpc_lockRangeFile(char *lockFile, OFF_T offset, OFF_T len, int block)
280             {
281             int fd;
282              
283 0 0         if ( (fd = open(lockFile, O_CREAT | O_RDWR, 0660)) < 0 ) {
284 0           bpc_logErrf("bpc_lockRangeFile: can't open/create lock file %s\n", lockFile);
285 0           return fd;
286             }
287 0 0         if ( bpc_lockRangeFd(fd, offset, len, block) ) {
288 0           close(fd);
289 0 0         if ( block ) {
290 0           bpc_logErrf("bpc_lockRangeFile: lock(%s) failed (errno = %d)\n", lockFile, errno);
291             }
292 0           return -1;
293             }
294 0           return fd;
295             }
296              
297 0           void bpc_unlockRangeFile(int lockFd)
298             {
299 0 0         if ( lockFd >= 0 ) close(lockFd);
300 0           }