1 /* Distributed under the 4-part Berkeley License */
2 
3 /*
4  * afflib_dir.cpp:
5  *
6  * Functions for the manipulation of the AFF directories
7  */
8 
9 #include "affconfig.h"
10 #include "afflib.h"
11 #include "afflib_i.h"
12 
aff_toc_free(AFFILE * af)13 int	aff_toc_free(AFFILE *af)
14 {
15     if(af->toc){
16 	for(int i=0;i<af->toc_count;i++){
17 	    if(af->toc[i].name) free(af->toc[i].name);
18 	}
19 	free(af->toc);
20 	af->toc = 0;
21 	af->toc_count = 0;
22     }
23     return 0;
24 }
25 
26 
aff_toc_print(AFFILE * af)27 void	aff_toc_print(AFFILE *af)
28 {
29     printf("AF DIRECTORY:\n");
30     for(int i=0;i<af->toc_count;i++){
31 	if(af->toc[i].name){
32 	    printf("%-32s @%" I64u " len: %" I64u " \n",af->toc[i].name, af->toc[i].offset,af->toc[i].segment_len);
33 	}
34     }
35 }
36 
37 #if 0
38 static int toc_sort(const void *a_,const void *b_)
39 {
40     const aff_toc_mem *a = (const aff_toc_mem *)a_;
41     const aff_toc_mem *b = (const aff_toc_mem *)b_;
42     if(a->offset < b->offset) return -1;
43     if(a->offset > b->offset) return 1;
44     return 0;
45 }
46 #endif
47 
aff_toc_append(AFFILE * af,const char * segname,uint64_t offset,uint64_t datalen)48 static int aff_toc_append(AFFILE *af,const char *segname,uint64_t offset,uint64_t datalen)
49 {
50     /* don't add AF_IGNORE segments to TOC */
51     if(!segname[0])
52         return 0;
53 
54     af->toc = (aff_toc_mem *)realloc(af->toc,sizeof(*af->toc)*(af->toc_count+1));
55     if(af->toc==0){
56 	(*af->error_reporter)("realloc() failed in aff_toc_append. toc_count=%d\n",af->toc_count);
57 	return -1;
58     }
59     af->toc[af->toc_count].offset = offset;
60     af->toc[af->toc_count].name = strdup(segname); // make a copy of the string
61     af->toc[af->toc_count].segment_len = aff_segment_overhead(segname)+datalen;
62     af->toc_count++;
63     return 0;
64 }
65 
66 /* Find an empty slot in the TOC in which to put the TOC.
67  * Otherwise add it to the end.
68  */
aff_toc_update(AFFILE * af,const char * segname,uint64_t offset,uint64_t datalen)69 void aff_toc_update(AFFILE *af,const char *segname,uint64_t offset,uint64_t datalen)
70 {
71     /* don't add AF_IGNORE segments to TOC */
72     if(!segname[0])
73         return;
74 
75     for(int i=0;i<af->toc_count;i++){
76 	if(af->toc[i].name==0 || strcmp(af->toc[i].name,segname)==0){
77 	    if(af->toc[i].name==0){	// if name was empty, copy it over
78 		af->toc[i].name = strdup(segname);
79 	    }
80 	    af->toc[i].offset  = offset;
81 	    af->toc[i].segment_len = aff_segment_overhead(segname)+datalen;
82 	    return;
83 	}
84     }
85     aff_toc_append(af,segname,offset,datalen);    /* Need to append it to the directory */
86 }
87 
88 /* exact compression settings are not saved in the AFF file, so make a best guess */
guess_compression(AFFILE * af,const char * segname,uint32_t flags)89 static void guess_compression(AFFILE *af, const char *segname, uint32_t flags)
90 {
91     /* ignore non-data segments */
92     if(af_segname_page_number(segname) < 0)
93 	return;
94 
95     if(flags & AF_PAGE_COMPRESSED)
96     {
97 	switch(flags & AF_PAGE_COMP_ALG_MASK)
98 	{
99 	case AF_PAGE_COMP_ALG_ZLIB:
100 	    af->compression_type = AF_COMPRESSION_ALG_ZLIB;
101 	    af->compression_level = (flags & AF_PAGE_COMP_MAX) ? AF_COMPRESSION_MAX : AF_COMPRESSION_DEFAULT;
102 	    break;
103 
104 	case AF_PAGE_COMP_ALG_LZMA:
105 	    af->compression_type = AF_COMPRESSION_ALG_LZMA;
106 	    af->compression_level = (flags & AF_PAGE_COMP_MAX) ? AF_COMPRESSION_MAX : AF_COMPRESSION_DEFAULT;
107 	    break;
108 	}
109     }
110     else
111     {
112 	af->compression_type = AF_COMPRESSION_ALG_NONE;
113 	af->compression_level = 0;
114     }
115 }
116 
117 /*
118  * aff_toc_build:
119  * Build the directory by reading the existing file.
120  * Notice that we know that we can simply append.
121  */
aff_toc_build(AFFILE * af)122 int	aff_toc_build(AFFILE *af)	// build the dir if we couldn't find it
123 {
124     aff_toc_free(af);			// clear the old one
125     af_rewind_seg(af);			// start at the beginning
126 
127     /* default compression settings */
128     af->compression_type = AF_COMPRESSION_ALG_ZLIB;
129     af->compression_level = AF_COMPRESSION_DEFAULT;
130 
131     // note: was malloc(0), but that causes problems under Borland
132     af->toc = (aff_toc_mem *)malloc(sizeof(aff_toc_mem));
133     while(1){
134 	char segname[AF_MAX_NAME_LEN];
135 	size_t segname_len=sizeof(segname);
136 	uint64_t pos = ftello(af->aseg);
137 	uint32_t flags = 0;
138 	size_t datalen=0;
139 
140 	errno = 0;
141 	int r = af_get_next_seg(af, segname, segname_len, &flags, 0, &datalen);
142 	switch(r){
143 	case AF_ERROR_NO_ERROR:
144 	    guess_compression(af, segname, flags);
145 	    if(aff_toc_append(af,segname,pos,datalen)){
146 		return -1; // malloc error?
147 	    }
148 	    break;
149 	case AF_ERROR_EOF:
150 	    return 0;			// end of file; no errors
151 	default: /* unknown error */
152 	    if(!errno)
153 		errno = EIO;
154 	    return r;			// send up the error code
155 	}
156     }
157     return AF_ERROR_NO_ERROR;
158 }
159 
160 /*
161  * return the named entry in the directory
162  */
163 
aff_toc(AFFILE * af,const char * segname)164 struct aff_toc_mem *aff_toc(AFFILE *af,const char *segname)
165 {
166     for(int i=0;i<af->toc_count;i++){
167 	if(af->toc[i].name && strcmp(af->toc[i].name,segname)==0) return &af->toc[i];
168     }
169     return 0;
170 }
171 
172 /* Delete something from the directory, but don't bother reallocating.*/
aff_toc_del(AFFILE * af,const char * segname)173 int aff_toc_del(AFFILE *af,const char *segname)
174 {
175     for(int i=0;i<af->toc_count;i++){
176 	if(af->toc[i].name && strcmp(af->toc[i].name,segname)==0){
177 	    free(af->toc[i].name);
178 	    af->toc[i].name=0;
179 	    return 0;			//  should only be in TOC once
180 	}
181     }
182     return -1;
183 }
184 
185 /* Finds the next segment starting at or beyond a given byte offset. */
186 /* Returns the TOC entry if found; null if not found. */
aff_toc_next_seg(AFFILE * af,uint64_t offset)187 struct aff_toc_mem *aff_toc_next_seg(AFFILE *af, uint64_t offset)
188 {
189     struct aff_toc_mem *next_seg = 0;
190     struct aff_toc_mem *end = af->toc + af->toc_count;
191 
192     for(struct aff_toc_mem *seg = af->toc; seg != end; seg++)
193     {
194         if(!seg->name)
195             continue;
196 
197         if(seg->offset >= offset && (!next_seg || seg->offset < next_seg->offset))
198             next_seg = seg;
199     }
200 
201     return next_seg;
202 }
203 
204 /* Finds the smallest unused block of at least min_size bytes. */
205 /* Returns 0 if found, -1 if not found. */
aff_toc_find_hole(AFFILE * af,uint64_t min_size,uint64_t * offset,uint64_t * size)206 int aff_toc_find_hole(AFFILE *af, uint64_t min_size, uint64_t *offset, uint64_t *size)
207 {
208     int ret = -1;
209     uint64_t search_offset = 0;
210     struct aff_toc_mem *seg;
211 
212     while((seg = aff_toc_next_seg(af, search_offset)))
213     {
214         uint64_t test_size = seg->offset - search_offset;
215 
216         if(test_size >= min_size && (ret < 0 || test_size < *size))
217         {
218             *offset = search_offset;
219             *size = test_size;
220             ret = 0;
221         }
222 
223         search_offset = seg->offset + seg->segment_len;
224     }
225 
226     return ret;
227 }
228