File Coverage

bpc_attribCache.c
Criterion Covered Total %
statement 0 392 0.0
branch 0 278 0.0
condition n/a
subroutine n/a
pod n/a
total 0 670 0.0


line stmt bran cond sub pod time code
1             /*
2             * Routines for caching multiple directories.
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             #define BPC_ATTRIBCACHE_DIR_COUNT_MAX (380)
23             #define BPC_ATTRIBCACHE_DIR_HT_SIZE (512)
24              
25 0           void bpc_attribCache_init(bpc_attribCache_info *ac, char *hostName, int backupNum, char *shareNameUM, int compress)
26             {
27 0           ac->backupNum = backupNum;
28 0           ac->compress = compress;
29 0           ac->cacheLruCnt = 0;
30 0           ac->bkupMergeList = NULL;
31 0           ac->bkupMergeCnt = 0;
32 0           ac->currentDir[0] = '\0';
33 0           ac->deltaInfo = NULL;
34 0           strncpy(ac->hostName, hostName, BPC_MAXPATHLEN);
35 0           ac->hostName[BPC_MAXPATHLEN - 1] = '\0';
36 0           strncpy(ac->shareNameUM, shareNameUM, BPC_MAXPATHLEN);
37 0           ac->shareNameUM[BPC_MAXPATHLEN - 1] = '\0';
38 0           bpc_fileNameEltMangle(ac->shareName, BPC_MAXPATHLEN, ac->shareNameUM);
39 0           ac->shareNameLen = strlen(ac->shareName);
40 0           snprintf(ac->hostDir, BPC_MAXPATHLEN, "%s/pc/%s", BPC_TopDir, hostName);
41 0           snprintf(ac->backupTopDir, BPC_MAXPATHLEN, "%s/pc/%s/%d", BPC_TopDir, hostName, ac->backupNum);
42 0           bpc_path_create(ac->backupTopDir);
43              
44 0           bpc_hashtable_create(&ac->attrHT, BPC_ATTRIBCACHE_DIR_HT_SIZE, sizeof(bpc_attribCache_dir));
45 0           bpc_hashtable_create(&ac->inodeHT, BPC_ATTRIBCACHE_DIR_HT_SIZE, sizeof(bpc_attribCache_dir));
46 0           }
47              
48 0           void bpc_attribCache_setDeltaInfo(bpc_attribCache_info *ac, bpc_deltaCount_info *deltaInfo)
49             {
50 0           ac->deltaInfo = deltaInfo;
51 0           }
52              
53             /*
54             * Caller is responsible for calling malloc for bkupList.
55             */
56 0           void bpc_attribCache_setMergeList(bpc_attribCache_info *ac, bpc_backup_info *bkupList, int bkupCnt)
57             {
58 0           ac->bkupMergeList = bkupList;
59 0           ac->bkupMergeCnt = bkupCnt;
60 0           }
61              
62 0           static void bpc_attribCache_destroyEntry(bpc_attribCache_dir *attr)
63             {
64 0           bpc_attrib_dirDestroy(&attr->dir);
65 0           }
66              
67 0           void bpc_attribCache_destroy(bpc_attribCache_info *ac)
68             {
69 0           bpc_hashtable_iterate(&ac->attrHT, (void*)bpc_attribCache_destroyEntry, NULL);
70 0           bpc_hashtable_destroy(&ac->attrHT);
71 0           bpc_hashtable_iterate(&ac->inodeHT, (void*)bpc_attribCache_destroyEntry, NULL);
72 0           bpc_hashtable_destroy(&ac->inodeHT);
73 0 0         if ( ac->bkupMergeList ) free(ac->bkupMergeList);
74 0           ac->bkupMergeList = NULL;
75 0           ac->bkupMergeCnt = 0;
76 0           }
77              
78 0           int bpc_attribCache_readOnly(bpc_attribCache_info *ac, int readOnly)
79             {
80 0 0         if ( readOnly >= 0 ) ac->readOnly = readOnly;
81 0           return ac->readOnly;
82             }
83              
84 0           void bpc_attribCache_setCurrentDirectory(bpc_attribCache_info *ac, char *dir)
85             {
86             char *p;
87 0           snprintf(ac->currentDir, BPC_MAXPATHLEN, "%s", dir);
88 0           p = ac->currentDir + strlen(ac->currentDir) - 1;
89 0 0         while ( p >= ac->currentDir && p[0] == '/' ) *p-- = '\0';
    0          
90 0           }
91              
92             /*
93             * Given a backup path, split it into the directory, file name, and path to the directory (starting
94             * with the share name, ie: relative to ac->backupTopDir).
95             *
96             * splitPath will strip initial "./" and trailing "/." or "/" before splitting the path, but isn't
97             * capable of handling paths with "/." in the middle, or ".." anywhere.
98             */
99 0           static void splitPath(bpc_attribCache_info *ac, char *dir, char *fileName, char *attribPath, char *path)
100             {
101 0           char *dirOrig = dir;
102             char fullPath[2*BPC_MAXPATHLEN];
103             size_t pathLen;
104              
105             /*
106             * remove initial "./"
107             */
108 0 0         while ( path[0] == '.' && path[1] == '/' ) {
    0          
109 0           path += 2;
110 0 0         while ( path[0] == '/' ) path++;
111             }
112              
113             /*
114             * if this is a relative path, prepend ac->currentDir (provided ac->currentDir is set)
115             */
116 0 0         if ( path[0] != '/' && ac->currentDir[0] ) {
    0          
117 0           snprintf(fullPath, sizeof(fullPath), "%s/%s", ac->currentDir, path);
118 0           path = fullPath;
119             }
120              
121             /*
122             * strip trailing "/." or "/"
123             */
124 0           pathLen = strlen(path);
125 0 0         while ( (pathLen > 1 && path[pathLen - 2] == '/' && path[pathLen - 1] == '.')
    0          
    0          
126 0 0         || (pathLen > 0 && path[pathLen - 1] == '/') ) {
    0          
127 0 0         if ( path != fullPath ) {
128 0           strncpy(fullPath, path, BPC_MAXPATHLEN);
129 0           path = fullPath;
130             }
131 0 0         if ( path[pathLen - 1] == '/' ) {
132 0           pathLen -= 1;
133             } else {
134 0           pathLen -= 2;
135             }
136 0           path[pathLen] = '\0';
137 0 0         if ( BPC_LogLevel >= 9 ) bpc_logMsgf("splitPath: trimming path = '%s'\n", path);
138             }
139 0 0         if ( !path[0] || (!path[1] && (path[0] == '.' || path[0] == '/')) ) {
    0          
    0          
    0          
140 0           strcpy(fileName, ac->shareNameUM);
141 0           strcpy(dir, "/");
142 0           strcpy(attribPath, "/attrib");
143             } else {
144             char *p;
145 0           int dirLen = BPC_MAXPATHLEN - ac->shareNameLen;
146              
147 0           strcpy(dir, ac->shareName);
148 0           dir += strlen(dir);
149 0 0         if ( (p = strrchr(path, '/')) ) {
150 0 0         if ( *path != '/' ) {
151 0           *dir++ = '/'; dirLen--;
152 0           *dir = '\0';
153             }
154 0           strcpy(fileName, p+1);
155 0           *p = '\0';
156 0           bpc_fileNameMangle(dir, dirLen, path);
157 0           *p = '/';
158             } else {
159 0           strcpy(fileName, path);
160             }
161 0           snprintf(attribPath, BPC_MAXPATHLEN, "%s/attrib", dirOrig);
162             }
163 0 0         if ( BPC_LogLevel >= 9 ) bpc_logMsgf("splitPath: returning dir = '%s', fileName = '%s', attrib = '%s' from path = '%s'\n",
164             dirOrig, fileName, attribPath, path);
165 0           }
166              
167 0           static void inodePath(UNUSED(bpc_attribCache_info *ac), char *indexStr, char *attribPath, char *attribFile, ino_t inode)
168             {
169 0           snprintf(attribPath, BPC_MAXPATHLEN, "inode/%02x", (unsigned int)(inode >> 17) & 0x7f);
170 0           snprintf(attribFile, BPC_MAXPATHLEN, "attrib%02x", (unsigned int)(inode >> 10) & 0x7f);
171             do {
172 0           bpc_byte2hex(indexStr, inode & 0xff);
173 0           indexStr += 2;
174 0           inode >>= 8;
175 0 0         } while ( inode );
176 0           *indexStr = '\0';
177 0           }
178              
179 0           static void bpc_attribCache_removeDeletedEntries(bpc_attrib_file *file, void *arg)
180             {
181 0           bpc_attribCache_dir *attr = (bpc_attribCache_dir*)arg;
182 0 0         if ( file->type != BPC_FTYPE_DELETED ) return;
183 0           attr->dirty = 1;
184 0           bpc_attrib_fileDestroy(file);
185 0           bpc_hashtable_nodeDelete(&attr->dir.filesHT, file);
186             }
187              
188 0           static bpc_attribCache_dir *bpc_attribCache_loadPath(bpc_attribCache_info *ac, char *fileName, char *path)
189             {
190             char dir[BPC_MAXPATHLEN], attribPath[BPC_MAXPATHLEN];
191             bpc_attribCache_dir *attr;
192             int attribPathLen, status;
193              
194 0           splitPath(ac, dir, fileName, attribPath, path);
195 0           attribPathLen = strlen(attribPath);
196              
197 0 0         if ( BPC_LogLevel >= 9 ) bpc_logMsgf("bpc_attribCache_loadPath: path = %s -> dir = %s, fileName = %s, attribPath = %s\n", path, dir, fileName, attribPath);
198              
199 0           attr = bpc_hashtable_find(&ac->attrHT, (uchar*)attribPath, attribPathLen, 1);
200              
201 0 0         if ( !attr || attr->key.key != attribPath ) {
    0          
202             /*
203             * cache hit - return the existing attributes
204             */
205 0 0         if ( attr ) attr->lruCnt = ac->cacheLruCnt++;
206 0           return attr;
207             }
208              
209 0 0         if ( !(attr->key.key = malloc(attribPathLen + 1)) ) {
210 0           bpc_logErrf("bpc_attribCache_loadPath: can't allocate %d bytes\n", attribPathLen + 1);
211 0           return NULL;
212             }
213 0           strcpy(attr->key.key, attribPath);
214 0           bpc_attrib_dirInit(&attr->dir, ac->compress);
215 0           attr->dirty = 0;
216 0           attr->dirOk = 0;
217 0           attr->lruCnt = ac->cacheLruCnt++;
218              
219 0 0         if ( ac->bkupMergeCnt > 0 ) {
220             int i;
221             char topDir[2*BPC_MAXPATHLEN], fullAttribPath[2*BPC_MAXPATHLEN];
222              
223             /*
224             * Merge multiple attrib files to create the "view" for this backup.
225             * There are two cases: merging forward for v3, or merging in reverse
226             * for v4+. bkupMergeList is already in the order we need.
227             */
228 0 0         for ( i = 0 ; i < ac->bkupMergeCnt ; i++ ) {
229             bpc_attrib_dir dir;
230             ssize_t entrySize;
231             char *entries, *fileName;
232              
233 0           snprintf(topDir, sizeof(topDir), "%s/pc/%s/%d", BPC_TopDir, ac->hostName, ac->bkupMergeList[i].num);
234 0           snprintf(fullAttribPath, sizeof(fullAttribPath), "%s/%s", topDir, attribPath);
235              
236 0           bpc_attrib_dirInit(&dir, ac->bkupMergeList[i].compress);
237 0 0         if ( (status = bpc_attrib_dirRead(&dir, topDir, attribPath, ac->bkupMergeList[i].num)) ) {
238 0 0         if ( ac->bkupMergeList[i].version < 4 ) {
239             char *p;
240 0           int attribDirExists = 1;
241             STRUCT_STAT st;
242              
243 0 0         if ( (p = strrchr(fullAttribPath, '/')) ) {
244 0           *p = '\0';
245 0 0         attribDirExists = !stat(fullAttribPath, &st) && S_ISDIR(st.st_mode);
    0          
246 0           *p = '/';
247             }
248 0 0         if ( i == ac->bkupMergeCnt - 1 && !attribDirExists ) {
    0          
249             /*
250             * For V3, if the last backup doesn't have a directory, then the merged view is empty
251             */
252 0           bpc_attrib_dirDestroy(&dir);
253 0           bpc_attrib_dirDestroy(&attr->dir);
254 0           bpc_attrib_dirInit(&attr->dir, ac->compress);
255 0           break;
256             }
257 0 0         if ( !attribDirExists ) {
258             /*
259             * nothing to update here - keep going
260             */
261 0           bpc_attrib_dirDestroy(&dir);
262 0           continue;
263             }
264             }
265 0           bpc_logErrf("bpc_attribCache_loadPath: bpc_attrib_dirRead(%s/%s) returned %d\n", topDir, attribPath, status);
266             }
267 0           entrySize = bpc_attrib_getEntries(&dir, NULL, 0);
268 0 0         if ( (entries = malloc(entrySize + 1)) && bpc_attrib_getEntries(&dir, entries, entrySize) == entrySize ) {
    0          
269 0 0         for ( fileName = entries ; fileName < entries + entrySize ; fileName += strlen(fileName) + 1 ) {
270 0           bpc_attrib_file *file = bpc_attrib_fileGet(&dir, fileName, 0);
271 0 0         if ( !file ) continue;
272 0 0         if ( file->type == BPC_FTYPE_DELETED ) {
273 0           bpc_attrib_fileDeleteName(&attr->dir, fileName);
274             } else {
275             bpc_attrib_file *fileDest;
276              
277 0 0         if ( !(fileDest = bpc_attrib_fileGet(&attr->dir, fileName, 1)) ) return NULL;
278 0 0         if ( fileDest->key.key == fileName ) {
279             /*
280             * new entry - initialize
281             */
282 0           bpc_attrib_fileInit(fileDest, fileName, 0);
283             }
284 0           bpc_attrib_fileCopy(fileDest, file);
285 0           fileDest->backupNum = ac->bkupMergeList[i].num;
286             }
287             }
288             } else {
289 0           bpc_logErrf("bpc_attribCache_loadPath(%s/%s): can't malloc %lu bytes for entries\n",
290             topDir, attribPath, (unsigned long)entrySize);
291 0 0         if ( entries ) free(entries);
292 0           bpc_attrib_dirDestroy(&dir);
293 0           return NULL;
294             }
295 0           free(entries);
296 0           bpc_attrib_dirDestroy(&dir);
297             }
298             } else {
299             /*
300             * non-merge case - read the single attrib file
301             */
302 0 0         if ( (status = bpc_attrib_dirRead(&attr->dir, ac->backupTopDir, attribPath, ac->backupNum)) ) {
303 0           bpc_logErrf("bpc_attribCache_loadPath: bpc_attrib_dirRead(%s, %s) returned %d\n", ac->backupTopDir, attribPath, status);
304             }
305             /*
306             * remove any extraneous BPC_FTYPE_DELETED file types
307             */
308 0           bpc_hashtable_iterate(&attr->dir.filesHT, (void*)bpc_attribCache_removeDeletedEntries, attr);
309             }
310 0 0         if ( bpc_hashtable_entryCount(&ac->attrHT) > BPC_ATTRIBCACHE_DIR_COUNT_MAX ) {
311 0           bpc_attribCache_flush(ac, 0, NULL);
312             }
313 0           return attr;
314             }
315              
316 0           static bpc_attribCache_dir *bpc_attribCache_loadInode(bpc_attribCache_info *ac, char *indexStr, ino_t inode)
317             {
318             char attribPath[BPC_MAXPATHLEN], attribDir[BPC_MAXPATHLEN], attribFile[BPC_MAXPATHLEN];
319             bpc_attribCache_dir *attr;
320             int attribPathLen, status;
321              
322 0           inodePath(ac, indexStr, attribDir, attribFile, inode);
323 0           attribPathLen = snprintf(attribPath, sizeof(attribPath), "%s/%s", attribDir, attribFile);
324              
325 0           attr = bpc_hashtable_find(&ac->inodeHT, (uchar*)attribPath, attribPathLen, 1);
326              
327 0 0         if ( !attr || attr->key.key != attribPath ) {
    0          
328 0 0         if ( attr ) attr->lruCnt = ac->cacheLruCnt++;
329 0           return attr;
330             }
331              
332             /*
333             * new entry - read the attrib file
334             */
335 0 0         if ( !(attr->key.key = malloc(attribPathLen + 1)) ) {
336 0           bpc_logErrf("bpc_attribCache_loadInode: can't allocate %d bytes\n", attribPathLen + 1);
337 0           return NULL;
338             }
339 0           strcpy(attr->key.key, attribPath);
340 0           bpc_attrib_dirInit(&attr->dir, ac->compress);
341 0           attr->dirty = 0;
342 0           attr->dirOk = 1;
343 0           attr->lruCnt = ac->cacheLruCnt++;
344 0 0         if ( ac->bkupMergeCnt > 0 ) {
345             int i;
346             char inodeDir[2*BPC_MAXPATHLEN], fullAttribPath[2*BPC_MAXPATHLEN];
347              
348             /*
349             * Merge multiple attrib files to create the "view" for this backup.
350             * There is only one case here, v4, since v3 didn't have inodes.
351             */
352 0 0         for ( i = 0 ; i < ac->bkupMergeCnt ; i++ ) {
353             bpc_attrib_dir dir;
354             ssize_t entrySize;
355             char *entries, *fileName;
356              
357 0           snprintf(inodeDir, sizeof(inodeDir), "%s/pc/%s/%d/%s", BPC_TopDir, ac->hostName, ac->bkupMergeList[i].num, attribDir);
358 0           snprintf(fullAttribPath, sizeof(fullAttribPath), "%s/%s", inodeDir, attribFile);
359              
360 0           bpc_attrib_dirInit(&dir, ac->bkupMergeList[i].compress);
361 0 0         if ( (status = bpc_attrib_dirRead(&dir, inodeDir, attribFile, ac->bkupMergeList[i].num)) ) {
362             STRUCT_STAT st;
363 0 0         int attribDirExists = !stat(inodeDir, &st) && S_ISDIR(st.st_mode);
    0          
364 0 0         if ( ac->bkupMergeList[i].version < 4 || !attribDirExists ) {
    0          
365             /*
366             * nothing to update here - keep going
367             */
368 0           bpc_attrib_dirDestroy(&dir);
369 0           continue;
370             }
371 0           bpc_logErrf("bpc_attribCache_loadInode: bpc_attrib_dirRead(%s/%s) returned %d\n", inodeDir, attribFile, status);
372             }
373 0           entrySize = bpc_attrib_getEntries(&dir, NULL, 0);
374 0 0         if ( (entries = malloc(entrySize + 1)) && bpc_attrib_getEntries(&dir, entries, entrySize) == entrySize ) {
    0          
375 0 0         for ( fileName = entries ; fileName < entries + entrySize ; fileName += strlen(fileName) + 1 ) {
376 0           bpc_attrib_file *file = bpc_attrib_fileGet(&dir, fileName, 0);
377 0 0         if ( !file ) continue;
378 0 0         if ( file->type == BPC_FTYPE_DELETED ) {
379 0           bpc_attrib_fileDeleteName(&attr->dir, fileName);
380             } else {
381             bpc_attrib_file *fileDest;
382              
383 0 0         if ( !(fileDest = bpc_attrib_fileGet(&attr->dir, fileName, 1)) ) return NULL;
384 0 0         if ( fileDest->key.key == fileName ) {
385             /*
386             * new entry - initialize
387             */
388 0           bpc_attrib_fileInit(fileDest, fileName, 0);
389             }
390 0           bpc_attrib_fileCopy(fileDest, file);
391             }
392             }
393             } else {
394 0           bpc_logErrf("bpc_attribCache_loadInode(%s): can't malloc %lu bytes for entries\n",
395             fullAttribPath, (unsigned long)entrySize);
396 0 0         if ( entries ) free(entries);
397 0           bpc_attrib_dirDestroy(&dir);
398 0           return NULL;
399             }
400 0           free(entries);
401 0           bpc_attrib_dirDestroy(&dir);
402             }
403             } else {
404             /*
405             * non-merge case - read the single attrib file
406             */
407             char inodeDir[2*BPC_MAXPATHLEN];
408 0           snprintf(inodeDir, sizeof(inodeDir), "%s/%s", ac->backupTopDir, attribDir);
409              
410 0 0         if ( (status = bpc_attrib_dirRead(&attr->dir, inodeDir, attribFile, ac->backupNum)) ) {
411 0           bpc_logErrf("bpc_attribCache_loadInode: bpc_attrib_dirRead(%s/%s) returned %d\n", inodeDir, attribFile, status);
412             }
413             }
414 0 0         if ( bpc_hashtable_entryCount(&ac->inodeHT) > BPC_ATTRIBCACHE_DIR_COUNT_MAX ) {
415 0           bpc_attribCache_flush(ac, 0, NULL);
416             }
417 0           return attr;
418             }
419              
420 0           bpc_attrib_file *bpc_attribCache_getFile(bpc_attribCache_info *ac, char *path, int allocate_if_missing, int dontReadInode)
421             {
422             char fileName[BPC_MAXPATHLEN];
423             bpc_attribCache_dir *attr;
424             bpc_attrib_file *file;
425              
426 0 0         if ( !(attr = bpc_attribCache_loadPath(ac, fileName, path)) ) return NULL;
427 0           attr->lruCnt = ac->cacheLruCnt++;
428 0 0         if ( !(file = bpc_attrib_fileGet(&attr->dir, fileName, allocate_if_missing)) ) return NULL;
429              
430 0 0         if ( allocate_if_missing && file->key.key == fileName ) {
    0          
431             /*
432             * new entry - initialize
433             */
434 0           bpc_attrib_fileInit(file, fileName, 0);
435 0           file->compress = ac->compress;
436             }
437 0 0         if ( dontReadInode || file->nlinks == 0 ) return file;
    0          
438              
439 0           return bpc_attribCache_getInode(ac, file->inode, allocate_if_missing);
440             }
441              
442 0           int bpc_attribCache_setFile(bpc_attribCache_info *ac, char *path, bpc_attrib_file *file, int dontOverwriteInode)
443             {
444             char fileName[BPC_MAXPATHLEN], indexStr[256];
445             bpc_attribCache_dir *attr, *attrInode;
446             bpc_attrib_file *fileDest;
447              
448 0 0         if ( !(attr = bpc_attribCache_loadPath(ac, fileName, path)) ) return -1;
449 0           attr->lruCnt = ac->cacheLruCnt++;
450 0           file->compress = ac->compress;
451              
452 0 0         if ( !(fileDest = bpc_attrib_fileGet(&attr->dir, fileName, 1)) ) return -1;
453              
454 0 0         if ( fileDest->key.key == fileName ) {
455             /*
456             * new entry - initialize
457             */
458 0           bpc_attrib_fileInit(fileDest, fileName, 0);
459             }
460              
461 0           bpc_attrib_fileCopy(fileDest, file);
462 0           attr->dirty = 1;
463 0 0         if ( file->nlinks > 0 ) {
464 0           bpc_attrib_file *inodeDest = bpc_attribCache_getInode(ac, file->inode, 0);
465 0 0         if ( !dontOverwriteInode || !inodeDest ) {
    0          
466 0           inodeDest = bpc_attribCache_getInode(ac, file->inode, 1);
467 0           bpc_attrib_fileCopyOpt(inodeDest, file, 0);
468              
469 0           attrInode = bpc_attribCache_loadInode(ac, indexStr, file->inode);
470 0           attrInode->dirty = 1;
471             /*
472             * remove the digest from the file attributes since the reference counting is reflected
473             * by the inode (can't do this up above since fileDest might be the same as file).
474             */
475 0           fileDest->digest.len = 0;
476 0           return 1;
477             } else {
478             /*
479             * remove the digest from the file attributes since the reference counting is reflected
480             * by the inode (can't do this up above since fileDest might be the same as file).
481             */
482 0           fileDest->digest.len = 0;
483 0           return 0;
484             }
485             }
486 0           return 1;
487             }
488              
489 0           int bpc_attribCache_deleteFile(bpc_attribCache_info *ac, char *path)
490             {
491             char fileName[BPC_MAXPATHLEN];
492             bpc_attribCache_dir *attr;
493              
494 0 0         if ( !(attr = bpc_attribCache_loadPath(ac, fileName, path)) ) return -1;
495 0           attr->lruCnt = ac->cacheLruCnt++;
496 0           bpc_attrib_fileDeleteName(&attr->dir, fileName);
497 0           attr->dirty = 1;
498 0           return 0;
499             }
500              
501 0           bpc_attrib_file *bpc_attribCache_getInode(bpc_attribCache_info *ac, ino_t inode, int allocate_if_missing)
502             {
503             char indexStr[256];
504             bpc_attribCache_dir *attr;
505             bpc_attrib_file *file;
506              
507 0 0         if ( !(attr = bpc_attribCache_loadInode(ac, indexStr, inode)) ) return NULL;
508 0           attr->lruCnt = ac->cacheLruCnt++;
509 0 0         if ( !(file = bpc_attrib_fileGet(&attr->dir, indexStr, allocate_if_missing)) ) return NULL;
510              
511 0 0         if ( allocate_if_missing && file->key.key == indexStr ) {
    0          
512             /*
513             * new entry - initialize
514             */
515 0           bpc_attrib_fileInit(file, indexStr, 0);
516 0           file->compress = ac->compress;
517             }
518 0           return file;
519             }
520              
521 0           int bpc_attribCache_setInode(bpc_attribCache_info *ac, ino_t inode, bpc_attrib_file *inodeSrc)
522             {
523             char indexStr[256];
524             bpc_attribCache_dir *attr;
525             bpc_attrib_file *inodeDest;
526              
527 0 0         if ( !(attr = bpc_attribCache_loadInode(ac, indexStr, inode)) ) return -1;
528 0           attr->lruCnt = ac->cacheLruCnt++;
529 0 0         if ( !(inodeDest = bpc_attrib_fileGet(&attr->dir, indexStr, 1)) ) return -1;
530              
531 0 0         if ( inodeDest->key.key == indexStr ) {
532             /*
533             * new entry - initialize
534             */
535 0           bpc_attrib_fileInit(inodeDest, indexStr, 0);
536             }
537 0           bpc_attrib_fileCopy(inodeDest, inodeSrc);
538 0           attr->dirty = 1;
539 0           return 0;
540             }
541              
542 0           int bpc_attribCache_deleteInode(bpc_attribCache_info *ac, ino_t inode)
543             {
544             char indexStr[256];
545             bpc_attribCache_dir *attr;
546              
547 0 0         if ( !(attr = bpc_attribCache_loadInode(ac, indexStr, inode)) ) return -1;
548 0           attr->lruCnt = ac->cacheLruCnt++;
549 0           bpc_attrib_fileDeleteName(&attr->dir, indexStr);
550 0           attr->dirty = 1;
551 0           return 0;
552             }
553              
554 0           int bpc_attribCache_getDirEntryCnt(bpc_attribCache_info *ac, char *path)
555             {
556             bpc_attribCache_dir *attr;
557             char fileName[BPC_MAXPATHLEN];
558 0           size_t pathLen = strlen(path);
559              
560             /*
561             * Append a fake file name so we actually open the directory's contents, not the directory entry one level up
562             */
563 0 0         if ( pathLen >= BPC_MAXPATHLEN - 3 ) return -1;
564 0           strcpy(path + pathLen, "/x");
565 0           attr = bpc_attribCache_loadPath(ac, fileName, path);
566 0           path[pathLen] = '\0';
567 0 0         if ( !attr ) return -1;
568 0           return bpc_hashtable_entryCount(&attr->dir.filesHT);
569             }
570              
571             typedef struct {
572             char *entries;
573             ssize_t entryIdx;
574             ssize_t entrySize;
575             } dirEntry_info;
576              
577 0           static void bpc_attribCache_getDirEntry(bpc_attrib_file *file, dirEntry_info *info)
578             {
579 0           ssize_t len = strlen(file->name) + 1;
580              
581 0 0         if ( info->entryIdx < 0 ) return;
582 0 0         if ( info->entries ) {
583 0 0         if ( info->entryIdx + len + (ssize_t)sizeof(ino_t) > info->entrySize ) {
584 0           info->entryIdx = -1;
585 0           return;
586             }
587 0           memcpy(info->entries + info->entryIdx, file->name, len);
588 0           info->entryIdx += len;
589 0           memcpy(info->entries + info->entryIdx, &file->inode, sizeof(ino_t));
590 0           info->entryIdx += sizeof(ino_t);
591             } else {
592 0           info->entryIdx += len + sizeof(ino_t);
593             }
594             }
595              
596 0           ssize_t bpc_attribCache_getDirEntries(bpc_attribCache_info *ac, char *path, char *entries, ssize_t entrySize)
597             {
598             bpc_attribCache_dir *attr;
599             char fileName[BPC_MAXPATHLEN], fullPath[2*BPC_MAXPATHLEN];
600             dirEntry_info info;
601 0           size_t pathLen = strlen(path);
602 0           ino_t inode = 0;
603              
604             /*
605             * Append a fake file name so we actually open the directory's contents, not the directory entry one level up
606             */
607 0 0         if ( pathLen >= BPC_MAXPATHLEN - 3 ) return -1;
608 0 0         if ( pathLen == 1 && path[0] == '.' ) {
    0          
609 0 0         if ( ac->currentDir[0] ) {
610 0           snprintf(fullPath, sizeof(fullPath), "%s/x", ac->currentDir);
611             } else {
612 0           strcpy(fullPath, "/x");
613             }
614 0           attr = bpc_attribCache_loadPath(ac, fileName, fullPath);
615 0           strcpy(path, ".");
616             } else {
617 0           snprintf(fullPath, BPC_MAXPATHLEN, "%s/x", path);
618 0           attr = bpc_attribCache_loadPath(ac, fileName, fullPath);
619             }
620 0 0         if ( !attr ) return -1;
621 0           attr->lruCnt = ac->cacheLruCnt++;
622              
623 0           info.entries = entries;
624 0           info.entryIdx = 0;
625 0           info.entrySize = entrySize;
626              
627 0 0         if ( entries && entrySize >= (ssize_t)(5 + 2 * sizeof(ino_t)) ) {
    0          
628 0           strcpy(info.entries + info.entryIdx, ".");
629 0           info.entryIdx += 2;
630             /* dummy inode number */
631 0           memcpy(info.entries + info.entryIdx, &inode, sizeof(inode));
632 0           info.entryIdx += sizeof(inode);
633              
634 0           strcpy(info.entries + info.entryIdx, "..");
635 0           info.entryIdx += 3;
636             /* dummy inode number */
637 0           memcpy(info.entries + info.entryIdx, &inode, sizeof(inode));
638 0           info.entryIdx += sizeof(inode);
639              
640             } else {
641 0           info.entryIdx += 5 + 2 * sizeof(ino_t);
642             }
643              
644 0           bpc_hashtable_iterate(&attr->dir.filesHT, (void*)bpc_attribCache_getDirEntry, &info);
645 0           return info.entryIdx;
646             }
647              
648             typedef struct {
649             char *path;
650             int pathLen;
651             int all;
652             bpc_attribCache_info *ac;
653             int entryCnt;
654             int entryIdx;
655             bpc_attribCache_dir **entries;
656             bpc_hashtable *ht;
657             int errorCnt;
658             } flush_info;
659              
660 0           static void bpc_attribCache_dirWrite(bpc_attribCache_dir *attr, flush_info *info)
661             {
662             int status;
663              
664 0 0         if ( !info->ac->readOnly && !info->all && info->path ) {
    0          
    0          
665 0 0         if ( BPC_LogLevel >= 9 ) bpc_logMsgf("bpc_attribCache_dirWrite: comparing %s vs key %s\n", info->path, attr->key.key);
666 0 0         if ( strncmp(info->path, attr->key.key, info->pathLen)
667 0 0         || (((char*)attr->key.key)[info->pathLen] != '/' && ((char*)attr->key.key)[info->pathLen] != '\0') ) {
    0          
668 0 0         if ( BPC_LogLevel >= 9 ) bpc_logMsgf("bpc_attribCache_dirWrite: skipping %s (doesn't match %s)\n", (char*)attr->key.key, info->path);
669 0           return;
670             }
671             }
672 0 0         if ( !info->ac->readOnly && attr->dirty ) {
    0          
673 0           bpc_digest *oldDigest = bpc_attrib_dirDigestGet(&attr->dir);
674 0 0         if ( BPC_LogLevel >= 6 ) bpc_logMsgf("bpc_attribCache_dirWrite: writing %s/%s with %d entries (oldDigest = 0x%02x%02x...)\n",
    0          
    0          
675 0           info->ac->backupTopDir, (char*)attr->key.key, bpc_hashtable_entryCount(&attr->dir.filesHT),
676 0           oldDigest ? oldDigest->digest[0] : 0, oldDigest ? oldDigest->digest[1] : 0);
677 0 0         if ( (status = bpc_attrib_dirWrite(info->ac->deltaInfo, &attr->dir, info->ac->backupTopDir, attr->key.key, oldDigest)) ) {
678 0           bpc_logErrf("bpc_attribCache_dirWrite: failed to write attributes for dir %s\n", (char*)attr->key.key);
679 0           info->errorCnt++;
680             }
681             }
682              
683             /*
684             * Now deallocate memory
685             */
686 0           bpc_attrib_dirDestroy(&attr->dir);
687 0 0         if ( attr->key.key ) free(attr->key.key);
688 0           bpc_hashtable_nodeDelete(info->ht, attr);
689             }
690              
691 0           static void bpc_attribCache_flush_lruListFill(bpc_attribCache_dir *attr, flush_info *info)
692             {
693 0 0         if ( info->entryIdx >= info->entryCnt ) return;
694 0           info->entries[info->entryIdx++] = attr;
695             }
696              
697 0           static int bpc_attribCache_flush_lruCompare(bpc_attribCache_dir **d1, bpc_attribCache_dir **d2)
698             {
699 0           return (*d1)->lruCnt - (*d2)->lruCnt;
700             }
701              
702             /*
703             * Build a list of all entries in the hash table, sorted by LRU count from lowest to highest
704             */
705 0           static void bpc_attribCache_flush_lruList(flush_info *info)
706             {
707             int i;
708              
709             /*
710             * allocate list of all entries
711             */
712 0           info->entryCnt = bpc_hashtable_entryCount(info->ht);
713 0           info->entryIdx = 0;
714 0           info->entries = NULL;
715 0 0         if ( info->entryCnt == 0 ) return;
716 0 0         if ( !(info->entries = malloc(info->entryCnt * sizeof(*info->entries))) ) {
717 0           bpc_logErrf("bpc_attribCache_flush_lruList: can't allocated %lu bytes\n", (unsigned long)info->entryCnt * sizeof(*info->entries));
718 0           return;
719             }
720 0           bpc_hashtable_iterate(info->ht, (void*)bpc_attribCache_flush_lruListFill, info);
721              
722             /*
723             * sort by lruCnt, from lowest to highest
724             */
725 0           qsort(info->entries, info->entryCnt, sizeof(*info->entries), (void*)bpc_attribCache_flush_lruCompare);
726              
727             /*
728             * Now flush the oldest half of the entries
729             */
730 0 0         for ( i = 0 ; i < info->entryCnt / 2 ; i++ ) {
731 0           bpc_attribCache_dirWrite(info->entries[i], info);
732             }
733              
734 0 0         if ( info->entries ) free(info->entries);
735             }
736              
737             /*
738             * Flush some or all of the cache. If all, then flush everything. If path is not NULL
739             * then just those entries that start with that path are flushed.
740             */
741 0           void bpc_attribCache_flush(bpc_attribCache_info *ac, int all, char *path)
742             {
743             flush_info info;
744             char attribPath[BPC_MAXPATHLEN];
745              
746 0           info.all = all;
747 0           info.ac = ac;
748 0 0         if ( path ) {
749             char pathDeep[BPC_MAXPATHLEN];
750             char fileName[BPC_MAXPATHLEN], dir[BPC_MAXPATHLEN];
751              
752 0           snprintf(pathDeep, BPC_MAXPATHLEN, "%s/foo", path);
753 0           splitPath(ac, dir, fileName, attribPath, pathDeep);
754 0           info.path = attribPath;
755 0           info.pathLen = strlen(info.path);
756             } else {
757 0           info.path = NULL;
758 0           info.pathLen = 0;
759             }
760 0           info.entryCnt = 0;
761 0           info.entryIdx = 0;
762 0           info.entries = NULL;
763 0           info.errorCnt = 0;
764              
765 0 0         if ( !all && !path ) {
    0          
766             /*
767             * flush the oldest half of the entries based on the lruCnt
768             */
769 0           info.ht = &ac->attrHT;
770 0           bpc_attribCache_flush_lruList(&info);
771 0           info.ht = &ac->inodeHT;
772 0           bpc_attribCache_flush_lruList(&info);
773             } else {
774 0           info.ht = &ac->attrHT;
775 0           bpc_hashtable_iterate(&ac->attrHT, (void*)bpc_attribCache_dirWrite, &info);
776 0           info.ht = &ac->inodeHT;
777 0           bpc_hashtable_iterate(&ac->inodeHT, (void*)bpc_attribCache_dirWrite, &info);
778             }
779 0 0         if ( info.errorCnt ) {
780             /*
781             * Any errors likely mean the deltas are probably out of sync with the
782             * file system, so request an fsck.
783             */
784 0           bpc_poolRefRequestFsck(ac->backupTopDir, 1);
785             }
786 0           }
787              
788             /*
789             * Returns the full mangled path, given a file path.
790             */
791 0           void bpc_attribCache_getFullMangledPath(bpc_attribCache_info *ac, char *path, char *dirName, int backupNum)
792             {
793             char *p;
794             int len;
795              
796             do {
797 0           p = dirName;
798 0 0         while ( dirName[0] == '.' && dirName[1] == '/' ) dirName += 2;
    0          
799 0 0         while ( dirName[0] == '/' ) dirName++;
800 0 0         } while ( p != dirName );
801              
802 0 0         if ( backupNum < 0 || ac->bkupMergeCnt <= 0 ) {
    0          
803 0           backupNum = ac->backupNum;
804             }
805              
806 0           len = snprintf(path, BPC_MAXPATHLEN, "%s/pc/%s/%d/%s", BPC_TopDir, ac->hostName, backupNum, ac->shareName);
807 0 0         if ( (dirName[0] == '/' && dirName[1] == '\0') || dirName[0] == '\0' || len >= BPC_MAXPATHLEN - 1 ) {
    0          
    0          
    0          
808 0           return;
809             }
810 0           path[len++] = '/';
811 0           bpc_fileNameMangle(path + len, BPC_MAXPATHLEN - len, dirName);
812             }