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