File Coverage

bpc_attribCache.c
Criterion Covered Total %
statement 0 404 0.0
branch 0 294 0.0
condition n/a
subroutine n/a
pod n/a
total 0 698 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, *entry;
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 0         if ( bpc_attrib_dirNeedRewrite(&dir) ) {
268 0           attr->dirty = 1;
269             }
270 0           entrySize = bpc_attrib_getEntries(&dir, NULL, 0);
271 0 0         if ( (entries = malloc(entrySize + 1)) && bpc_attrib_getEntries(&dir, entries, entrySize) == entrySize ) {
    0          
272 0 0         for ( entry = entries ; entry < entries + entrySize ; entry += strlen(entry) + 1 ) {
273 0           bpc_attrib_file *file = bpc_attrib_fileGet(&dir, entry, 0);
274 0 0         if ( !file ) continue;
275 0 0         if ( file->type == BPC_FTYPE_DELETED ) {
276 0           bpc_attrib_fileDeleteName(&attr->dir, entry);
277             } else {
278             bpc_attrib_file *fileDest;
279              
280 0 0         if ( !(fileDest = bpc_attrib_fileGet(&attr->dir, entry, 1)) ) return NULL;
281 0 0         if ( fileDest->key.key == entry ) {
282             /*
283             * new entry - initialize
284             */
285 0           bpc_attrib_fileInit(fileDest, entry, 0);
286             }
287 0           bpc_attrib_fileCopy(fileDest, file);
288 0           fileDest->backupNum = ac->bkupMergeList[i].num;
289             }
290             }
291             } else {
292 0           bpc_logErrf("bpc_attribCache_loadPath(%s/%s): can't malloc %lu bytes for entries\n",
293             topDir, attribPath, (unsigned long)entrySize);
294 0 0         if ( entries ) free(entries);
295 0           bpc_attrib_dirDestroy(&dir);
296 0           return NULL;
297             }
298 0           free(entries);
299 0           bpc_attrib_dirDestroy(&dir);
300             }
301             } else {
302             /*
303             * non-merge case - read the single attrib file
304             */
305 0 0         if ( (status = bpc_attrib_dirRead(&attr->dir, ac->backupTopDir, attribPath, ac->backupNum)) ) {
306 0           bpc_logErrf("bpc_attribCache_loadPath: bpc_attrib_dirRead(%s, %s) returned %d\n", ac->backupTopDir, attribPath, status);
307             }
308 0 0         if ( bpc_attrib_dirNeedRewrite(&attr->dir) ) {
309 0           attr->dirty = 1;
310             }
311             /*
312             * remove any extraneous BPC_FTYPE_DELETED file types
313             */
314 0           bpc_hashtable_iterate(&attr->dir.filesHT, (void*)bpc_attribCache_removeDeletedEntries, attr);
315             }
316 0 0         if ( attr->dirty ) {
317 0 0         if ( BPC_LogLevel >= 8 ) bpc_logMsgf("bpc_attribCache_loadPath: will rewrite path = %s -> dir = %s, fileName = %s, attribPath = %s\n", path, dir, fileName, attribPath);
318             }
319 0 0         if ( bpc_hashtable_entryCount(&ac->attrHT) > BPC_ATTRIBCACHE_DIR_COUNT_MAX ) {
320 0           bpc_attribCache_flush(ac, 0, NULL);
321             }
322 0           return attr;
323             }
324              
325 0           static bpc_attribCache_dir *bpc_attribCache_loadInode(bpc_attribCache_info *ac, char *indexStr, ino_t inode)
326             {
327             char attribPath[BPC_MAXPATHLEN], attribDir[BPC_MAXPATHLEN], attribFile[BPC_MAXPATHLEN];
328             bpc_attribCache_dir *attr;
329             int attribPathLen, status;
330              
331 0           inodePath(ac, indexStr, attribDir, attribFile, inode);
332 0           attribPathLen = snprintf(attribPath, sizeof(attribPath), "%s/%s", attribDir, attribFile);
333              
334 0           attr = bpc_hashtable_find(&ac->inodeHT, (uchar*)attribPath, attribPathLen, 1);
335              
336 0 0         if ( !attr || attr->key.key != attribPath ) {
    0          
337 0 0         if ( attr ) attr->lruCnt = ac->cacheLruCnt++;
338 0           return attr;
339             }
340              
341             /*
342             * new entry - read the attrib file
343             */
344 0 0         if ( !(attr->key.key = malloc(attribPathLen + 1)) ) {
345 0           bpc_logErrf("bpc_attribCache_loadInode: can't allocate %d bytes\n", attribPathLen + 1);
346 0           return NULL;
347             }
348 0           strcpy(attr->key.key, attribPath);
349 0           bpc_attrib_dirInit(&attr->dir, ac->compress);
350 0           attr->dirty = 0;
351 0           attr->dirOk = 1;
352 0           attr->lruCnt = ac->cacheLruCnt++;
353 0 0         if ( ac->bkupMergeCnt > 0 ) {
354             int i;
355             char inodeDir[2*BPC_MAXPATHLEN], fullAttribPath[2*BPC_MAXPATHLEN];
356              
357             /*
358             * Merge multiple attrib files to create the "view" for this backup.
359             * There is only one case here, v4, since v3 didn't have inodes.
360             */
361 0 0         for ( i = 0 ; i < ac->bkupMergeCnt ; i++ ) {
362             bpc_attrib_dir dir;
363             ssize_t entrySize;
364             char *entries, *entry;
365              
366 0           snprintf(inodeDir, sizeof(inodeDir), "%s/pc/%s/%d/%s", BPC_TopDir, ac->hostName, ac->bkupMergeList[i].num, attribDir);
367 0           snprintf(fullAttribPath, sizeof(fullAttribPath), "%s/%s", inodeDir, attribFile);
368              
369 0           bpc_attrib_dirInit(&dir, ac->bkupMergeList[i].compress);
370 0 0         if ( (status = bpc_attrib_dirRead(&dir, inodeDir, attribFile, ac->bkupMergeList[i].num)) ) {
371             STRUCT_STAT st;
372 0 0         int attribDirExists = !stat(inodeDir, &st) && S_ISDIR(st.st_mode);
    0          
373 0 0         if ( ac->bkupMergeList[i].version < 4 || !attribDirExists ) {
    0          
374             /*
375             * nothing to update here - keep going
376             */
377 0           bpc_attrib_dirDestroy(&dir);
378 0           continue;
379             }
380 0           bpc_logErrf("bpc_attribCache_loadInode: bpc_attrib_dirRead(%s/%s) returned %d\n", inodeDir, attribFile, status);
381             }
382 0 0         if ( bpc_attrib_dirNeedRewrite(&dir) ) {
383 0           attr->dirty = 1;
384             }
385 0           entrySize = bpc_attrib_getEntries(&dir, NULL, 0);
386 0 0         if ( (entries = malloc(entrySize + 1)) && bpc_attrib_getEntries(&dir, entries, entrySize) == entrySize ) {
    0          
387 0 0         for ( entry = entries ; entry < entries + entrySize ; entry += strlen(entry) + 1 ) {
388 0           bpc_attrib_file *file = bpc_attrib_fileGet(&dir, entry, 0);
389 0 0         if ( !file ) continue;
390 0 0         if ( file->type == BPC_FTYPE_DELETED ) {
391 0           bpc_attrib_fileDeleteName(&attr->dir, entry);
392             } else {
393             bpc_attrib_file *fileDest;
394              
395 0 0         if ( !(fileDest = bpc_attrib_fileGet(&attr->dir, entry, 1)) ) return NULL;
396 0 0         if ( fileDest->key.key == entry ) {
397             /*
398             * new entry - initialize
399             */
400 0           bpc_attrib_fileInit(fileDest, entry, 0);
401             }
402 0           bpc_attrib_fileCopy(fileDest, file);
403             }
404             }
405             } else {
406 0           bpc_logErrf("bpc_attribCache_loadInode(%s): can't malloc %lu bytes for entries\n",
407             fullAttribPath, (unsigned long)entrySize);
408 0 0         if ( entries ) free(entries);
409 0           bpc_attrib_dirDestroy(&dir);
410 0           return NULL;
411             }
412 0           free(entries);
413 0           bpc_attrib_dirDestroy(&dir);
414             }
415             } else {
416             /*
417             * non-merge case - read the single attrib file
418             */
419             char inodeDir[2*BPC_MAXPATHLEN];
420 0           snprintf(inodeDir, sizeof(inodeDir), "%s/%s", ac->backupTopDir, attribDir);
421              
422 0 0         if ( (status = bpc_attrib_dirRead(&attr->dir, inodeDir, attribFile, ac->backupNum)) ) {
423 0           bpc_logErrf("bpc_attribCache_loadInode: bpc_attrib_dirRead(%s/%s) returned %d\n", inodeDir, attribFile, status);
424             }
425 0 0         if ( bpc_attrib_dirNeedRewrite(&attr->dir) ) {
426 0           attr->dirty = 1;
427             }
428             }
429 0 0         if ( attr->dirty ) {
430 0 0         if ( BPC_LogLevel >= 8 ) bpc_logMsgf("bpc_attribCache_loadInode: will rewrite path = %s -> dir = %s, fileName = %s\n", attribPath, attribDir, attribFile);
431             }
432 0 0         if ( bpc_hashtable_entryCount(&ac->inodeHT) > BPC_ATTRIBCACHE_DIR_COUNT_MAX ) {
433 0           bpc_attribCache_flush(ac, 0, NULL);
434             }
435 0           return attr;
436             }
437              
438 0           bpc_attrib_file *bpc_attribCache_getFile(bpc_attribCache_info *ac, char *path, int allocate_if_missing, int dontReadInode)
439             {
440             char fileName[BPC_MAXPATHLEN];
441             bpc_attribCache_dir *attr;
442             bpc_attrib_file *file;
443              
444 0 0         if ( !(attr = bpc_attribCache_loadPath(ac, fileName, path)) ) return NULL;
445 0           attr->lruCnt = ac->cacheLruCnt++;
446 0 0         if ( !(file = bpc_attrib_fileGet(&attr->dir, fileName, allocate_if_missing)) ) return NULL;
447              
448 0 0         if ( allocate_if_missing && file->key.key == fileName ) {
    0          
449             /*
450             * new entry - initialize
451             */
452 0           bpc_attrib_fileInit(file, fileName, 0);
453 0           file->compress = ac->compress;
454             }
455 0 0         if ( dontReadInode || file->nlinks == 0 ) return file;
    0          
456              
457 0           return bpc_attribCache_getInode(ac, file->inode, allocate_if_missing);
458             }
459              
460 0           int bpc_attribCache_setFile(bpc_attribCache_info *ac, char *path, bpc_attrib_file *file, int dontOverwriteInode)
461             {
462             char fileName[BPC_MAXPATHLEN], indexStr[256];
463             bpc_attribCache_dir *attr, *attrInode;
464             bpc_attrib_file *fileDest;
465              
466 0 0         if ( !(attr = bpc_attribCache_loadPath(ac, fileName, path)) ) return -1;
467 0           attr->lruCnt = ac->cacheLruCnt++;
468 0           file->compress = ac->compress;
469              
470 0 0         if ( !(fileDest = bpc_attrib_fileGet(&attr->dir, fileName, 1)) ) return -1;
471              
472 0 0         if ( fileDest->key.key == fileName ) {
473             /*
474             * new entry - initialize
475             */
476 0           bpc_attrib_fileInit(fileDest, fileName, 0);
477             }
478              
479 0           bpc_attrib_fileCopy(fileDest, file);
480 0           attr->dirty = 1;
481 0 0         if ( file->nlinks > 0 ) {
482 0           bpc_attrib_file *inodeDest = bpc_attribCache_getInode(ac, file->inode, 0);
483 0 0         if ( !dontOverwriteInode || !inodeDest ) {
    0          
484 0           inodeDest = bpc_attribCache_getInode(ac, file->inode, 1);
485 0           bpc_attrib_fileCopyOpt(inodeDest, file, 0);
486              
487 0           attrInode = bpc_attribCache_loadInode(ac, indexStr, file->inode);
488 0           attrInode->dirty = 1;
489             /*
490             * remove the digest from the file attributes since the reference counting is reflected
491             * by the inode (can't do this up above since fileDest might be the same as file).
492             */
493 0           fileDest->digest.len = 0;
494 0           return 1;
495             } else {
496             /*
497             * remove the digest from the file attributes since the reference counting is reflected
498             * by the inode (can't do this up above since fileDest might be the same as file).
499             */
500 0           fileDest->digest.len = 0;
501 0           return 0;
502             }
503             }
504 0           return 1;
505             }
506              
507 0           int bpc_attribCache_deleteFile(bpc_attribCache_info *ac, char *path)
508             {
509             char fileName[BPC_MAXPATHLEN];
510             bpc_attribCache_dir *attr;
511              
512 0 0         if ( !(attr = bpc_attribCache_loadPath(ac, fileName, path)) ) return -1;
513 0           attr->lruCnt = ac->cacheLruCnt++;
514 0           bpc_attrib_fileDeleteName(&attr->dir, fileName);
515 0           attr->dirty = 1;
516 0           return 0;
517             }
518              
519 0           bpc_attrib_file *bpc_attribCache_getInode(bpc_attribCache_info *ac, ino_t inode, int allocate_if_missing)
520             {
521             char indexStr[256];
522             bpc_attribCache_dir *attr;
523             bpc_attrib_file *file;
524              
525 0 0         if ( !(attr = bpc_attribCache_loadInode(ac, indexStr, inode)) ) return NULL;
526 0           attr->lruCnt = ac->cacheLruCnt++;
527 0 0         if ( !(file = bpc_attrib_fileGet(&attr->dir, indexStr, allocate_if_missing)) ) return NULL;
528              
529 0 0         if ( allocate_if_missing && file->key.key == indexStr ) {
    0          
530             /*
531             * new entry - initialize
532             */
533 0           bpc_attrib_fileInit(file, indexStr, 0);
534 0           file->compress = ac->compress;
535             }
536 0           return file;
537             }
538              
539 0           int bpc_attribCache_setInode(bpc_attribCache_info *ac, ino_t inode, bpc_attrib_file *inodeSrc)
540             {
541             char indexStr[256];
542             bpc_attribCache_dir *attr;
543             bpc_attrib_file *inodeDest;
544              
545 0 0         if ( !(attr = bpc_attribCache_loadInode(ac, indexStr, inode)) ) return -1;
546 0           attr->lruCnt = ac->cacheLruCnt++;
547 0 0         if ( !(inodeDest = bpc_attrib_fileGet(&attr->dir, indexStr, 1)) ) return -1;
548              
549 0 0         if ( inodeDest->key.key == indexStr ) {
550             /*
551             * new entry - initialize
552             */
553 0           bpc_attrib_fileInit(inodeDest, indexStr, 0);
554             }
555 0           bpc_attrib_fileCopy(inodeDest, inodeSrc);
556 0           attr->dirty = 1;
557 0           return 0;
558             }
559              
560 0           int bpc_attribCache_deleteInode(bpc_attribCache_info *ac, ino_t inode)
561             {
562             char indexStr[256];
563             bpc_attribCache_dir *attr;
564              
565 0 0         if ( !(attr = bpc_attribCache_loadInode(ac, indexStr, inode)) ) return -1;
566 0           attr->lruCnt = ac->cacheLruCnt++;
567 0           bpc_attrib_fileDeleteName(&attr->dir, indexStr);
568 0           attr->dirty = 1;
569 0           return 0;
570             }
571              
572 0           int bpc_attribCache_getDirEntryCnt(bpc_attribCache_info *ac, char *path)
573             {
574             bpc_attribCache_dir *attr;
575             char fileName[BPC_MAXPATHLEN];
576 0           size_t pathLen = strlen(path);
577              
578             /*
579             * Append a fake file name so we actually open the directory's contents, not the directory entry one level up
580             */
581 0 0         if ( pathLen >= BPC_MAXPATHLEN - 3 ) return -1;
582 0           strcpy(path + pathLen, "/x");
583 0           attr = bpc_attribCache_loadPath(ac, fileName, path);
584 0           path[pathLen] = '\0';
585 0 0         if ( !attr ) return -1;
586 0           return bpc_hashtable_entryCount(&attr->dir.filesHT);
587             }
588              
589             typedef struct {
590             char *entries;
591             ssize_t entryIdx;
592             ssize_t entrySize;
593             } dirEntry_info;
594              
595 0           static void bpc_attribCache_getDirEntry(bpc_attrib_file *file, dirEntry_info *info)
596             {
597 0           ssize_t len = strlen(file->name) + 1;
598              
599 0 0         if ( info->entryIdx < 0 ) return;
600 0 0         if ( info->entries ) {
601 0 0         if ( info->entryIdx + len + (ssize_t)sizeof(ino_t) > info->entrySize ) {
602 0           info->entryIdx = -1;
603 0           return;
604             }
605 0           memcpy(info->entries + info->entryIdx, file->name, len);
606 0           info->entryIdx += len;
607 0           memcpy(info->entries + info->entryIdx, &file->inode, sizeof(ino_t));
608 0           info->entryIdx += sizeof(ino_t);
609             } else {
610 0           info->entryIdx += len + sizeof(ino_t);
611             }
612             }
613              
614 0           ssize_t bpc_attribCache_getDirEntries(bpc_attribCache_info *ac, char *path, char *entries, ssize_t entrySize)
615             {
616             bpc_attribCache_dir *attr;
617             char fileName[BPC_MAXPATHLEN], fullPath[2*BPC_MAXPATHLEN];
618             dirEntry_info info;
619 0           size_t pathLen = strlen(path);
620 0           ino_t inode = 0;
621              
622             /*
623             * Append a fake file name so we actually open the directory's contents, not the directory entry one level up
624             */
625 0 0         if ( pathLen >= BPC_MAXPATHLEN - 3 ) return -1;
626 0 0         if ( pathLen == 1 && path[0] == '.' ) {
    0          
627 0 0         if ( ac->currentDir[0] ) {
628 0           snprintf(fullPath, sizeof(fullPath), "%s/x", ac->currentDir);
629             } else {
630 0           strcpy(fullPath, "/x");
631             }
632 0           attr = bpc_attribCache_loadPath(ac, fileName, fullPath);
633 0           strcpy(path, ".");
634             } else {
635 0           snprintf(fullPath, BPC_MAXPATHLEN, "%s/x", path);
636 0           attr = bpc_attribCache_loadPath(ac, fileName, fullPath);
637             }
638 0 0         if ( !attr ) return -1;
639 0           attr->lruCnt = ac->cacheLruCnt++;
640              
641 0           info.entries = entries;
642 0           info.entryIdx = 0;
643 0           info.entrySize = entrySize;
644              
645 0 0         if ( entries && entrySize >= (ssize_t)(5 + 2 * sizeof(ino_t)) ) {
    0          
646 0           strcpy(info.entries + info.entryIdx, ".");
647 0           info.entryIdx += 2;
648             /* dummy inode number */
649 0           memcpy(info.entries + info.entryIdx, &inode, sizeof(inode));
650 0           info.entryIdx += sizeof(inode);
651              
652 0           strcpy(info.entries + info.entryIdx, "..");
653 0           info.entryIdx += 3;
654             /* dummy inode number */
655 0           memcpy(info.entries + info.entryIdx, &inode, sizeof(inode));
656 0           info.entryIdx += sizeof(inode);
657              
658             } else {
659 0           info.entryIdx += 5 + 2 * sizeof(ino_t);
660             }
661              
662 0           bpc_hashtable_iterate(&attr->dir.filesHT, (void*)bpc_attribCache_getDirEntry, &info);
663 0           return info.entryIdx;
664             }
665              
666             typedef struct {
667             char *path;
668             int pathLen;
669             int all;
670             bpc_attribCache_info *ac;
671             int entryCnt;
672             int entryIdx;
673             bpc_attribCache_dir **entries;
674             bpc_hashtable *ht;
675             int errorCnt;
676             } flush_info;
677              
678 0           static void bpc_attribCache_dirWrite(bpc_attribCache_dir *attr, flush_info *info)
679             {
680             int status;
681              
682 0 0         if ( !info->ac->readOnly && !info->all && info->path ) {
    0          
    0          
683 0 0         if ( BPC_LogLevel >= 9 ) bpc_logMsgf("bpc_attribCache_dirWrite: comparing %s vs key %s\n", info->path, attr->key.key);
684 0 0         if ( strncmp(info->path, attr->key.key, info->pathLen)
685 0 0         || (((char*)attr->key.key)[info->pathLen] != '/' && ((char*)attr->key.key)[info->pathLen] != '\0') ) {
    0          
686 0 0         if ( BPC_LogLevel >= 9 ) bpc_logMsgf("bpc_attribCache_dirWrite: skipping %s (doesn't match %s)\n", (char*)attr->key.key, info->path);
687 0           return;
688             }
689             }
690 0 0         if ( !info->ac->readOnly && attr->dirty ) {
    0          
691 0           bpc_digest *oldDigest = bpc_attrib_dirDigestGet(&attr->dir);
692 0 0         if ( BPC_LogLevel >= 6 ) bpc_logMsgf("bpc_attribCache_dirWrite: writing %s/%s with %d entries (oldDigest = 0x%02x%02x...)\n",
    0          
    0          
693 0           info->ac->backupTopDir, (char*)attr->key.key, bpc_hashtable_entryCount(&attr->dir.filesHT),
694 0           oldDigest ? oldDigest->digest[0] : 0, oldDigest ? oldDigest->digest[1] : 0);
695 0 0         if ( (status = bpc_attrib_dirWrite(info->ac->deltaInfo, &attr->dir, info->ac->backupTopDir, attr->key.key, oldDigest)) ) {
696 0           bpc_logErrf("bpc_attribCache_dirWrite: failed to write attributes for dir %s\n", (char*)attr->key.key);
697 0           info->errorCnt++;
698             }
699             }
700              
701             /*
702             * Now deallocate memory
703             */
704 0           bpc_attrib_dirDestroy(&attr->dir);
705 0 0         if ( attr->key.key ) free(attr->key.key);
706 0           bpc_hashtable_nodeDelete(info->ht, attr);
707             }
708              
709 0           static void bpc_attribCache_flush_lruListFill(bpc_attribCache_dir *attr, flush_info *info)
710             {
711 0 0         if ( info->entryIdx >= info->entryCnt ) return;
712 0           info->entries[info->entryIdx++] = attr;
713             }
714              
715 0           static int bpc_attribCache_flush_lruCompare(bpc_attribCache_dir **d1, bpc_attribCache_dir **d2)
716             {
717 0           return (*d1)->lruCnt - (*d2)->lruCnt;
718             }
719              
720             /*
721             * Build a list of all entries in the hash table, sorted by LRU count from lowest to highest
722             */
723 0           static void bpc_attribCache_flush_lruList(flush_info *info)
724             {
725             int i;
726              
727             /*
728             * allocate list of all entries
729             */
730 0           info->entryCnt = bpc_hashtable_entryCount(info->ht);
731 0           info->entryIdx = 0;
732 0           info->entries = NULL;
733 0 0         if ( info->entryCnt == 0 ) return;
734 0 0         if ( !(info->entries = malloc(info->entryCnt * sizeof(*info->entries))) ) {
735 0           bpc_logErrf("bpc_attribCache_flush_lruList: can't allocated %lu bytes\n", (unsigned long)info->entryCnt * sizeof(*info->entries));
736 0           return;
737             }
738 0           bpc_hashtable_iterate(info->ht, (void*)bpc_attribCache_flush_lruListFill, info);
739              
740             /*
741             * sort by lruCnt, from lowest to highest
742             */
743 0           qsort(info->entries, info->entryCnt, sizeof(*info->entries), (void*)bpc_attribCache_flush_lruCompare);
744              
745             /*
746             * Now flush the oldest half of the entries
747             */
748 0 0         for ( i = 0 ; i < info->entryCnt / 2 ; i++ ) {
749 0           bpc_attribCache_dirWrite(info->entries[i], info);
750             }
751              
752 0 0         if ( info->entries ) free(info->entries);
753             }
754              
755             /*
756             * Flush some or all of the cache. If all, then flush everything. If path is not NULL
757             * then just those entries that start with that path are flushed.
758             */
759 0           void bpc_attribCache_flush(bpc_attribCache_info *ac, int all, char *path)
760             {
761             flush_info info;
762             char attribPath[BPC_MAXPATHLEN];
763              
764 0           info.all = all;
765 0           info.ac = ac;
766 0 0         if ( path ) {
767             char pathDeep[BPC_MAXPATHLEN];
768             char fileName[BPC_MAXPATHLEN], dir[BPC_MAXPATHLEN];
769              
770 0           snprintf(pathDeep, BPC_MAXPATHLEN, "%s/foo", path);
771 0           splitPath(ac, dir, fileName, attribPath, pathDeep);
772 0           info.path = attribPath;
773 0           info.pathLen = strlen(info.path);
774             } else {
775 0           info.path = NULL;
776 0           info.pathLen = 0;
777             }
778 0           info.entryCnt = 0;
779 0           info.entryIdx = 0;
780 0           info.entries = NULL;
781 0           info.errorCnt = 0;
782              
783 0 0         if ( !all && !path ) {
    0          
784             /*
785             * flush the oldest half of the entries based on the lruCnt
786             */
787 0           info.ht = &ac->attrHT;
788 0           bpc_attribCache_flush_lruList(&info);
789 0           info.ht = &ac->inodeHT;
790 0           bpc_attribCache_flush_lruList(&info);
791             } else {
792 0           info.ht = &ac->attrHT;
793 0           bpc_hashtable_iterate(&ac->attrHT, (void*)bpc_attribCache_dirWrite, &info);
794 0           info.ht = &ac->inodeHT;
795 0           bpc_hashtable_iterate(&ac->inodeHT, (void*)bpc_attribCache_dirWrite, &info);
796             }
797 0 0         if ( info.errorCnt ) {
798             /*
799             * Any errors likely mean the deltas are probably out of sync with the
800             * file system, so request an fsck.
801             */
802 0           bpc_poolRefRequestFsck(ac->backupTopDir, 1);
803             }
804 0           }
805              
806             /*
807             * Returns the full mangled path, given a file path.
808             */
809 0           void bpc_attribCache_getFullMangledPath(bpc_attribCache_info *ac, char *path, char *dirName, int backupNum)
810             {
811             char *p;
812             int len;
813              
814             do {
815 0           p = dirName;
816 0 0         while ( dirName[0] == '.' && dirName[1] == '/' ) dirName += 2;
    0          
817 0 0         while ( dirName[0] == '/' ) dirName++;
818 0 0         } while ( p != dirName );
819              
820 0 0         if ( backupNum < 0 || ac->bkupMergeCnt <= 0 ) {
    0          
821 0           backupNum = ac->backupNum;
822             }
823              
824 0           len = snprintf(path, BPC_MAXPATHLEN, "%s/pc/%s/%d/%s", BPC_TopDir, ac->hostName, backupNum, ac->shareName);
825 0 0         if ( (dirName[0] == '/' && dirName[1] == '\0') || dirName[0] == '\0' || len >= BPC_MAXPATHLEN - 1 ) {
    0          
    0          
    0          
826 0           return;
827             }
828 0           path[len++] = '/';
829 0           bpc_fileNameMangle(path + len, BPC_MAXPATHLEN - len, dirName);
830             }