tdb/common/freelist.c

説明を見る。
00001  /* 
00002    Unix SMB/CIFS implementation.
00003 
00004    trivial database library
00005 
00006    Copyright (C) Andrew Tridgell              1999-2005
00007    Copyright (C) Paul `Rusty' Russell              2000
00008    Copyright (C) Jeremy Allison                    2000-2003
00009    
00010      ** NOTE! The following LGPL license applies to the tdb
00011      ** library. This does NOT imply that all of Samba is released
00012      ** under the LGPL
00013    
00014    This library is free software; you can redistribute it and/or
00015    modify it under the terms of the GNU Lesser General Public
00016    License as published by the Free Software Foundation; either
00017    version 2 of the License, or (at your option) any later version.
00018 
00019    This library is distributed in the hope that it will be useful,
00020    but WITHOUT ANY WARRANTY; without even the implied warranty of
00021    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00022    Lesser General Public License for more details.
00023 
00024    You should have received a copy of the GNU Lesser General Public
00025    License along with this library; if not, write to the Free Software
00026    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00027 */
00028 
00029 #include "tdb_private.h"
00030 
00031 /* read a freelist record and check for simple errors */
00032 int rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct list_struct *rec)
00033 {
00034         if (tdb->methods->tdb_read(tdb, off, rec, sizeof(*rec),DOCONV()) == -1)
00035                 return -1;
00036 
00037         if (rec->magic == TDB_MAGIC) {
00038                 /* this happens when a app is showdown while deleting a record - we should
00039                    not completely fail when this happens */
00040                 TDB_LOG((tdb, TDB_DEBUG_WARNING, "rec_free_read non-free magic 0x%x at offset=%d - fixing\n", 
00041                          rec->magic, off));
00042                 rec->magic = TDB_FREE_MAGIC;
00043                 if (tdb->methods->tdb_write(tdb, off, rec, sizeof(*rec)) == -1)
00044                         return -1;
00045         }
00046 
00047         if (rec->magic != TDB_FREE_MAGIC) {
00048                 /* Ensure ecode is set for log fn. */
00049                 tdb->ecode = TDB_ERR_CORRUPT;
00050                 TDB_LOG((tdb, TDB_DEBUG_WARNING, "rec_free_read bad magic 0x%x at offset=%d\n", 
00051                            rec->magic, off));
00052                 return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
00053         }
00054         if (tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0) != 0)
00055                 return -1;
00056         return 0;
00057 }
00058 
00059 
00060 
00061 /* Remove an element from the freelist.  Must have alloc lock. */
00062 static int remove_from_freelist(struct tdb_context *tdb, tdb_off_t off, tdb_off_t next)
00063 {
00064         tdb_off_t last_ptr, i;
00065 
00066         /* read in the freelist top */
00067         last_ptr = FREELIST_TOP;
00068         while (tdb_ofs_read(tdb, last_ptr, &i) != -1 && i != 0) {
00069                 if (i == off) {
00070                         /* We've found it! */
00071                         return tdb_ofs_write(tdb, last_ptr, &next);
00072                 }
00073                 /* Follow chain (next offset is at start of record) */
00074                 last_ptr = i;
00075         }
00076         TDB_LOG((tdb, TDB_DEBUG_FATAL,"remove_from_freelist: not on list at off=%d\n", off));
00077         return TDB_ERRCODE(TDB_ERR_CORRUPT, -1);
00078 }
00079 
00080 
00081 /* update a record tailer (must hold allocation lock) */
00082 static int update_tailer(struct tdb_context *tdb, tdb_off_t offset,
00083                          const struct list_struct *rec)
00084 {
00085         tdb_off_t totalsize;
00086 
00087         /* Offset of tailer from record header */
00088         totalsize = sizeof(*rec) + rec->rec_len;
00089         return tdb_ofs_write(tdb, offset + totalsize - sizeof(tdb_off_t),
00090                          &totalsize);
00091 }
00092 
00093 /* Add an element into the freelist. Merge adjacent records if
00094    neccessary. */
00095 int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec)
00096 {
00097         tdb_off_t right, left;
00098 
00099         /* Allocation and tailer lock */
00100         if (tdb_lock(tdb, -1, F_WRLCK) != 0)
00101                 return -1;
00102 
00103         /* set an initial tailer, so if we fail we don't leave a bogus record */
00104         if (update_tailer(tdb, offset, rec) != 0) {
00105                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed!\n"));
00106                 goto fail;
00107         }
00108 
00109         /* Look right first (I'm an Australian, dammit) */
00110         right = offset + sizeof(*rec) + rec->rec_len;
00111         if (right + sizeof(*rec) <= tdb->map_size) {
00112                 struct list_struct r;
00113 
00114                 if (tdb->methods->tdb_read(tdb, right, &r, sizeof(r), DOCONV()) == -1) {
00115                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: right read failed at %u\n", right));
00116                         goto left;
00117                 }
00118 
00119                 /* If it's free, expand to include it. */
00120                 if (r.magic == TDB_FREE_MAGIC) {
00121                         if (remove_from_freelist(tdb, right, r.next) == -1) {
00122                                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: right free failed at %u\n", right));
00123                                 goto left;
00124                         }
00125                         rec->rec_len += sizeof(r) + r.rec_len;
00126                 }
00127         }
00128 
00129 left:
00130         /* Look left */
00131         left = offset - sizeof(tdb_off_t);
00132         if (left > TDB_DATA_START(tdb->header.hash_size)) {
00133                 struct list_struct l;
00134                 tdb_off_t leftsize;
00135                 
00136                 /* Read in tailer and jump back to header */
00137                 if (tdb_ofs_read(tdb, left, &leftsize) == -1) {
00138                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left offset read failed at %u\n", left));
00139                         goto update;
00140                 }
00141 
00142                 /* it could be uninitialised data */
00143                 if (leftsize == 0 || leftsize == TDB_PAD_U32) {
00144                         goto update;
00145                 }
00146 
00147                 left = offset - leftsize;
00148 
00149                 /* Now read in record */
00150                 if (tdb->methods->tdb_read(tdb, left, &l, sizeof(l), DOCONV()) == -1) {
00151                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left read failed at %u (%u)\n", left, leftsize));
00152                         goto update;
00153                 }
00154 
00155                 /* If it's free, expand to include it. */
00156                 if (l.magic == TDB_FREE_MAGIC) {
00157                         if (remove_from_freelist(tdb, left, l.next) == -1) {
00158                                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left free failed at %u\n", left));
00159                                 goto update;
00160                         } else {
00161                                 offset = left;
00162                                 rec->rec_len += leftsize;
00163                         }
00164                 }
00165         }
00166 
00167 update:
00168         if (update_tailer(tdb, offset, rec) == -1) {
00169                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed at %u\n", offset));
00170                 goto fail;
00171         }
00172 
00173         /* Now, prepend to free list */
00174         rec->magic = TDB_FREE_MAGIC;
00175 
00176         if (tdb_ofs_read(tdb, FREELIST_TOP, &rec->next) == -1 ||
00177             tdb_rec_write(tdb, offset, rec) == -1 ||
00178             tdb_ofs_write(tdb, FREELIST_TOP, &offset) == -1) {
00179                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free record write failed at offset=%d\n", offset));
00180                 goto fail;
00181         }
00182 
00183         /* And we're done. */
00184         tdb_unlock(tdb, -1, F_WRLCK);
00185         return 0;
00186 
00187  fail:
00188         tdb_unlock(tdb, -1, F_WRLCK);
00189         return -1;
00190 }
00191 
00192 
00193 /* 
00194    the core of tdb_allocate - called when we have decided which
00195    free list entry to use
00196  */
00197 static tdb_off_t tdb_allocate_ofs(struct tdb_context *tdb, tdb_len_t length, tdb_off_t rec_ptr,
00198                                 struct list_struct *rec, tdb_off_t last_ptr)
00199 {
00200         struct list_struct newrec;
00201         tdb_off_t newrec_ptr;
00202 
00203         memset(&newrec, '\0', sizeof(newrec));
00204 
00205         /* found it - now possibly split it up  */
00206         if (rec->rec_len > length + MIN_REC_SIZE) {
00207                 /* Length of left piece */
00208                 length = TDB_ALIGN(length, TDB_ALIGNMENT);
00209                 
00210                 /* Right piece to go on free list */
00211                 newrec.rec_len = rec->rec_len - (sizeof(*rec) + length);
00212                 newrec_ptr = rec_ptr + sizeof(*rec) + length;
00213                 
00214                 /* And left record is shortened */
00215                 rec->rec_len = length;
00216         } else {
00217                 newrec_ptr = 0;
00218         }
00219         
00220         /* Remove allocated record from the free list */
00221         if (tdb_ofs_write(tdb, last_ptr, &rec->next) == -1) {
00222                 return 0;
00223         }
00224         
00225         /* Update header: do this before we drop alloc
00226            lock, otherwise tdb_free() might try to
00227            merge with us, thinking we're free.
00228            (Thanks Jeremy Allison). */
00229         rec->magic = TDB_MAGIC;
00230         if (tdb_rec_write(tdb, rec_ptr, rec) == -1) {
00231                 return 0;
00232         }
00233         
00234         /* Did we create new block? */
00235         if (newrec_ptr) {
00236                 /* Update allocated record tailer (we
00237                    shortened it). */
00238                 if (update_tailer(tdb, rec_ptr, rec) == -1) {
00239                         return 0;
00240                 }
00241                 
00242                 /* Free new record */
00243                 if (tdb_free(tdb, newrec_ptr, &newrec) == -1) {
00244                         return 0;
00245                 }
00246         }
00247         
00248         /* all done - return the new record offset */
00249         return rec_ptr;
00250 }
00251 
00252 /* allocate some space from the free list. The offset returned points
00253    to a unconnected list_struct within the database with room for at
00254    least length bytes of total data
00255 
00256    0 is returned if the space could not be allocated
00257  */
00258 tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_struct *rec)
00259 {
00260         tdb_off_t rec_ptr, last_ptr, newrec_ptr;
00261         struct {
00262                 tdb_off_t rec_ptr, last_ptr;
00263                 tdb_len_t rec_len;
00264         } bestfit;
00265 
00266         if (tdb_lock(tdb, -1, F_WRLCK) == -1)
00267                 return 0;
00268 
00269         /* Extra bytes required for tailer */
00270         length += sizeof(tdb_off_t);
00271 
00272  again:
00273         last_ptr = FREELIST_TOP;
00274 
00275         /* read in the freelist top */
00276         if (tdb_ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1)
00277                 goto fail;
00278 
00279         bestfit.rec_ptr = 0;
00280         bestfit.last_ptr = 0;
00281         bestfit.rec_len = 0;
00282 
00283         /* 
00284            this is a best fit allocation strategy. Originally we used
00285            a first fit strategy, but it suffered from massive fragmentation
00286            issues when faced with a slowly increasing record size.
00287          */
00288         while (rec_ptr) {
00289                 if (rec_free_read(tdb, rec_ptr, rec) == -1) {
00290                         goto fail;
00291                 }
00292 
00293                 if (rec->rec_len >= length) {
00294                         if (bestfit.rec_ptr == 0 ||
00295                             rec->rec_len < bestfit.rec_len) {
00296                                 bestfit.rec_len = rec->rec_len;
00297                                 bestfit.rec_ptr = rec_ptr;
00298                                 bestfit.last_ptr = last_ptr;
00299                                 /* consider a fit to be good enough if
00300                                    we aren't wasting more than half
00301                                    the space */
00302                                 if (bestfit.rec_len < 2*length) {
00303                                         break;
00304                                 }
00305                         }
00306                 }
00307 
00308                 /* move to the next record */
00309                 last_ptr = rec_ptr;
00310                 rec_ptr = rec->next;
00311         }
00312 
00313         if (bestfit.rec_ptr != 0) {
00314                 if (rec_free_read(tdb, bestfit.rec_ptr, rec) == -1) {
00315                         goto fail;
00316                 }
00317 
00318                 newrec_ptr = tdb_allocate_ofs(tdb, length, bestfit.rec_ptr, rec, bestfit.last_ptr);
00319                 tdb_unlock(tdb, -1, F_WRLCK);
00320                 return newrec_ptr;
00321         }
00322 
00323         /* we didn't find enough space. See if we can expand the
00324            database and if we can then try again */
00325         if (tdb_expand(tdb, length + sizeof(*rec)) == 0)
00326                 goto again;
00327  fail:
00328         tdb_unlock(tdb, -1, F_WRLCK);
00329         return 0;
00330 }
00331 

Sambaに対してSat Aug 29 21:23:26 2009に生成されました。  doxygen 1.4.7