nfs-ganesha 1.4

cache_content_misc.c

Go to the documentation of this file.
00001 /*
00002  * vim:expandtab:shiftwidth=8:tabstop=8:
00003  *
00004  * Copyright CEA/DAM/DIF  (2008)
00005  * contributeur : Philippe DENIEL   philippe.deniel@cea.fr
00006  *                Thomas LEIBOVICI  thomas.leibovici@cea.fr
00007  *
00008  *
00009  * This program is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU Lesser General Public
00011  * License as published by the Free Software Foundation; either
00012  * version 3 of the License, or (at your option) any later version.
00013  * 
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  * Lesser General Public License for more details.
00018  * 
00019  * You should have received a copy of the GNU Lesser General Public
00020  * License along with this library; if not, write to the Free Software
00021  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00022  * 
00023  * ---------------------------------------
00024  */
00025 
00038 #ifdef HAVE_CONFIG_H
00039 #include "config.h"
00040 #endif
00041 
00042 #ifdef _SOLARIS
00043 #include "solaris_port.h"
00044 #define NAME_MAX         255
00045 #include <sys/statvfs.h>        /* For statfs */
00046 #endif                          /* _SOLARIS */
00047 
00048 #include "fsal.h"
00049 #include "LRU_List.h"
00050 #include "log.h"
00051 #include "HashData.h"
00052 #include "HashTable.h"
00053 #include "cache_inode.h"
00054 #include "cache_content.h"
00055 #include "nfs_exports.h"
00056 
00057 #include <unistd.h>
00058 #include <sys/types.h>
00059 #include <sys/param.h>
00060 #include <time.h>
00061 #include <pthread.h>
00062 #include <string.h>
00063 #include <libgen.h>
00064 
00065 #ifdef _LINUX
00066 #include <sys/vfs.h>            /* For statfs */
00067 #endif
00068 
00069 #ifdef _APPLE
00070 #include <sys/param.h>          /* For Statfs */
00071 #include <sys/mount.h>
00072 #endif
00073 
00074 unsigned int cache_content_dir_errno;
00075 
00076 /* HashFileID4 : creates a 16bits hash of the 64bits fileid4 buffer.
00077  *
00078  * @param fileid4 [IN] 64bits fileid to be hashed.
00079  */
00080 short HashFileID4(u_int64_t fileid4)
00081 {
00082   int i;
00083   short hash_val = 0;
00084 
00085   for(i = 0; i <= 56; i += 8)
00086     {
00087 #define ALPHABET_LEN      16
00088 #define PRIME_16BITS   65521
00089 
00090       hash_val = (hash_val * ALPHABET_LEN + ((fileid4 >> i) & 0xFF)) % PRIME_16BITS;
00091     }
00092 
00093   return hash_val;
00094 }
00095 
00111 cache_content_status_t cache_content_create_name(char *path,
00112                                                  cache_content_nametype_t type,
00113                                                  fsal_op_context_t * pcontext,
00114                                                  cache_entry_t * pentry_inode,
00115                                                  cache_content_client_t * pclient)
00116 {
00117   fsal_status_t fsal_status;
00118   u_int64_t fileid4;            /* Don't want to include nfs_prot.h at this level */
00119   fsal_handle_t *pfsal_handle = NULL;
00120   struct fsal_handle_desc fh_desc;
00121   char entrydir[MAXPATHLEN];
00122   int i, nb_char;
00123   short hash_val;
00124 
00125   pfsal_handle = &pentry_inode->handle;
00126 
00127   fh_desc.start = (caddr_t)&fileid4;
00128   fh_desc.len = sizeof(fileid4);
00129   /* Get the digest for the handle, for computing an entry name */
00130   fsal_status = FSAL_DigestHandle(FSAL_GET_EXP_CTX(pcontext),
00131                                   FSAL_DIGEST_FILEID4, pfsal_handle, &fh_desc);
00132 
00133   if(FSAL_IS_ERROR(fsal_status))
00134     return CACHE_CONTENT_FSAL_ERROR;
00135 
00136   /* computes a 16bits hash of the 64bits fileid4 buffer */
00137   hash_val = HashFileID4(fileid4);
00138 
00139   /* for limiting the number of entries into each datacache directory
00140    * we create 256 subdirectories on 2 levels, depending on the entry's fileid.
00141    */
00142   nb_char = snprintf(entrydir, MAXPATHLEN, "%s/export_id=%d", pclient->cache_dir, 0);
00143 
00144   for(i = 0; i <= 8; i += 8)
00145     {
00146       /* concatenation of hashval */
00147       nb_char += snprintf((char *)(entrydir + nb_char), MAXPATHLEN - nb_char,
00148                           "/%02hhX", (char)((hash_val >> i) & 0xFF));
00149 
00150       /* creating the directory if necessary */
00151       if((mkdir(entrydir, 0750) != 0) && (errno != EEXIST))
00152         {
00153           return CACHE_CONTENT_LOCAL_CACHE_ERROR;
00154         }
00155     }
00156 
00157   /* Create files for caching the entry: index file */
00158   switch (type)
00159     {
00160     case CACHE_CONTENT_DATA_FILE:
00161       snprintf(path, MAXPATHLEN, "%s/node=%llx.data", entrydir,
00162                (unsigned long long)fileid4);
00163       break;
00164 
00165     case CACHE_CONTENT_INDEX_FILE:
00166       snprintf(path, MAXPATHLEN, "%s/node=%llx.index", entrydir,
00167                (unsigned long long)fileid4);
00168       break;
00169 
00170     case CACHE_CONTENT_DIR:
00171       snprintf(path, MAXPATHLEN, "%s/export_id=%d", pclient->cache_dir, 0);
00172       break;
00173 
00174     default:
00175       return CACHE_CONTENT_INVALID_ARGUMENT;
00176     }
00177 
00178   return CACHE_CONTENT_SUCCESS;
00179 }                               /* cache_content_create_name */
00180 
00192 int cache_content_get_export_id(char *dirname)
00193 {
00194   int exportid;
00195 
00196   if(strncmp(dirname, "export_id=", strlen("export_id=")))
00197     return -1;
00198 
00199   if(sscanf(dirname, "export_id=%d", &exportid) == 0)
00200     return -1;
00201   else
00202     return exportid;
00203 }                               /* cache_content_get_export_id */
00204 
00216 u_int64_t cache_content_get_inum(char *filename)
00217 {
00218   unsigned long long inum;
00219   char buff[MAXNAMLEN];
00220   char *bname = NULL;
00221 
00222   /* splits the dirent->d_name into path and filename */
00223   strncpy(buff, filename, MAXNAMLEN);
00224   bname = basename(buff);
00225 
00226   if(strncmp(bname, "node=", strlen("node=")))
00227     return 0;
00228 
00229   if(strlen(bname) < 5)
00230     return 0;
00231 
00232   if(strncmp(bname + strlen(bname) - 5, "index", NAME_MAX))
00233     return 0;
00234 
00235   if(sscanf(bname, "node=%llx.index", &inum) == 0)
00236     return 0;
00237   else
00238     return (u_int64_t) inum;
00239 }                               /* cache_content_get_inum */
00240 
00254 int cache_content_get_datapath(char *basepath, u_int64_t inum, char *datapath)
00255 {
00256   short hash_val;
00257 
00258   hash_val = HashFileID4(inum);
00259 
00260   snprintf(datapath, MAXPATHLEN, "%s/%02hhX/%02hhX/node=%llx.data", basepath,
00261            (char)((hash_val) & 0xFF),
00262            (char)((hash_val >> 8) & 0xFF), (unsigned long long)inum);
00263 
00264   LogFullDebug(COMPONENT_CACHE_CONTENT, "cache_content_get_datapath : datapath ----> %s",
00265                   datapath);
00266 
00267   /* it is ok, we now return 0 */
00268   return 0;
00269 }                               /* cache_content_get_datapath */
00270 
00284 off_t cache_content_recover_size(char *basepath, u_int64_t inum)
00285 {
00286   char path[MAXPATHLEN];
00287   struct stat buffstat;
00288 
00289   cache_content_get_datapath(basepath, inum, path);
00290 
00291   if(stat(path, &buffstat) != 0)
00292     {
00293       LogCrit(COMPONENT_CACHE_CONTENT,
00294           "Failure in cache_content_recover_size while stat on local cache: path=%s errno = %u",
00295            path, errno);
00296 
00297       return -1;
00298     }
00299 
00300   LogFullDebug(COMPONENT_CACHE_CONTENT, "path ----> %s %"PRIu64, path, buffstat.st_size);
00301 
00302   /* Stat is ok, we now return the size */
00303   return buffstat.st_size;
00304 }                               /* cache_content_recover_size */
00305 
00317 off_t cache_content_get_cached_size(cache_content_entry_t * pentry)
00318 {
00319   struct stat buffstat;
00320 
00321   if(stat(pentry->local_fs_entry.cache_path_data, &buffstat) != 0)
00322     {
00323       LogCrit(COMPONENT_CACHE_CONTENT,
00324           "Failure in cache_content_get_cached_size while stat on local cache: path=%s errno = %u",
00325            pentry->local_fs_entry.cache_path_index, errno);
00326 
00327       return -1;
00328     }
00329 
00330   /* Stat is ok, we now return the size */
00331 
00332   return buffstat.st_size;
00333 
00334 }                               /* cache_content_get_cached_size */
00335 
00347 cache_inode_status_t cache_content_error_convert(cache_content_status_t status)
00348 {
00349   cache_inode_status_t converted_status;
00350 
00351   switch (status)
00352     {
00353     case CACHE_CONTENT_SUCCESS:
00354       converted_status = CACHE_INODE_SUCCESS;
00355       break;
00356 
00357     case CACHE_CONTENT_INVALID_ARGUMENT:
00358       converted_status = CACHE_INODE_INVALID_ARGUMENT;
00359       break;
00360 
00361     case CACHE_CONTENT_BAD_CACHE_INODE_ENTRY:
00362       converted_status = CACHE_INODE_INVALID_ARGUMENT;
00363       break;
00364 
00365     case CACHE_CONTENT_ENTRY_EXISTS:
00366       converted_status = CACHE_INODE_ENTRY_EXISTS;
00367       break;
00368 
00369     case CACHE_CONTENT_FSAL_ERROR:
00370       converted_status = CACHE_INODE_FSAL_ERROR;
00371       break;
00372 
00373     case CACHE_CONTENT_LOCAL_CACHE_ERROR:
00374       converted_status = CACHE_INODE_CACHE_CONTENT_ERROR;
00375       break;
00376 
00377     case CACHE_CONTENT_MALLOC_ERROR:
00378       converted_status = CACHE_INODE_MALLOC_ERROR;
00379       break;
00380 
00381     case CACHE_CONTENT_LRU_ERROR:
00382       converted_status = CACHE_INODE_LRU_ERROR;
00383       break;
00384 
00385     default:
00386       converted_status = CACHE_INODE_INVALID_ARGUMENT;
00387       break;
00388     }
00389 
00390   return converted_status;
00391 }                               /* cache_content_error_convert */
00392 
00405 size_t cache_content_fsal_size_convert(fsal_size_t size, cache_content_status_t * pstatus)
00406 {
00407   size_t taille;
00408 
00409   *pstatus = CACHE_CONTENT_SUCCESS;
00410   taille = (size_t) size;
00411 
00412   return taille;
00413 }                               /* cache_content_fsal_size_convert */
00414 
00427 cache_content_status_t cache_content_prepare_directories(exportlist_t * pexportlist,
00428                                                          char *cache_dir,
00429                                                          cache_content_status_t * pstatus)
00430 {
00431   exportlist_t *pexport = NULL;
00432   char cache_sub_dir[MAXPATHLEN];
00433 
00434   /* Does the cache root directory exist ? */
00435   if(access(cache_dir, F_OK) == -1)
00436     {
00437       /* Create the cache root directory */
00438       if(mkdir(cache_dir, 0750) == -1)
00439         return CACHE_CONTENT_LOCAL_CACHE_ERROR;
00440     }
00441 
00442   /* Create the sub directories if needed */
00443   for(pexport = pexportlist; pexport != NULL; pexport = pexport->next)
00444     {
00445       /* Create a directory only if the export entry is to be datya cached */
00446       if(pexport->options & EXPORT_OPTION_USE_DATACACHE)
00447         {
00448           snprintf(cache_sub_dir, MAXPATHLEN, "%s/export_id=%d", cache_dir, 0);
00449 
00450           if(access(cache_sub_dir, F_OK) == -1)
00451             {
00452               /* Create the cache  directory */
00453               if(mkdir(cache_sub_dir, 0750) == -1)
00454                 return CACHE_CONTENT_LOCAL_CACHE_ERROR;
00455             }
00456         }
00457     }
00458 
00459   /* If this point is reached, everything went ok */
00460   return CACHE_CONTENT_SUCCESS;
00461 }                               /* cache_content_prepare_directories */
00462 
00482 cache_content_status_t cache_content_check_threshold(char *datacache_path,
00483                                                      unsigned int threshold_min,
00484                                                      unsigned int threshold_max,
00485                                                      int *p_bool_overflow,
00486                                                      unsigned long *p_blocks_to_lwm)
00487 {
00488   char fspath[MAXPATHLEN];
00489 #ifdef _SOLARIS
00490   struct statvfs info_fs;
00491 #else
00492   struct statfs info_fs;
00493 #endif
00494   unsigned long total_user_blocs, dispo_hw __attribute__((unused)), dispo_lw;
00495   double tx_used, hw, lw;
00496 
00497   /* defensive checks */
00498 
00499   if(!datacache_path || !p_bool_overflow || !p_blocks_to_lwm
00500      || (threshold_min > threshold_max) || (threshold_max > 100))
00501     return CACHE_CONTENT_INVALID_ARGUMENT;
00502 
00503   /* cross mountpoint */
00504 
00505   snprintf(fspath, MAXPATHLEN, "%s/.", datacache_path);
00506 
00507   /* retieve FS info */
00508 
00509   if(statfs(fspath, &info_fs) != 0)
00510     {
00511       LogCrit(COMPONENT_CACHE_CONTENT, "Error getting local filesystem info: path=%s errno=%u", fspath,
00512                  errno);
00513       return CACHE_CONTENT_LOCAL_CACHE_ERROR;
00514     }
00515 
00516   /* Compute thresholds and total block count.
00517    * Those formulas are based on the df's code:
00518    * used = f_blocks - available_to_root
00519    *      = f_blocks - f_bfree
00520    * total = used + available
00521    *       = f_blocks - f_bfree + f_bavail
00522    */
00523   hw = (double)threshold_max;   /* cast to double */
00524   lw = (double)threshold_min;   /* cast to double */
00525 
00526   total_user_blocs = (info_fs.f_blocks + info_fs.f_bavail - info_fs.f_bfree);
00527   dispo_hw = (unsigned long)(((100.0 - hw) * total_user_blocs) / 100.0);
00528   dispo_lw = (unsigned long)(((100.0 - lw) * total_user_blocs) / 100.0);
00529 
00530   tx_used = 100.0 * ((double)info_fs.f_blocks - (double)info_fs.f_bfree) /
00531       ((double)info_fs.f_blocks + (double)info_fs.f_bavail - (double)info_fs.f_bfree);
00532 
00533   LogEvent(COMPONENT_CACHE_CONTENT,
00534                   "Datacache: %s: %.2f%% used, low_wm = %.2f%%, high_wm = %.2f%%",
00535                   datacache_path, tx_used, lw, hw);
00536 
00537   /* threshold test */
00538 
00539   /* if the threshold is under high watermark, nothing to do */
00540 
00541   if(tx_used < hw)
00542     {
00543       *p_bool_overflow = FALSE;
00544       *p_blocks_to_lwm = 0;
00545       LogEvent(COMPONENT_CACHE_CONTENT, "Datacache: no purge needed");
00546     }
00547   else
00548     {
00549       *p_bool_overflow = TRUE;
00550       *p_blocks_to_lwm = dispo_lw - info_fs.f_bavail;
00551       LogEvent(COMPONENT_CACHE_CONTENT,
00552                       "Datacache: need to purge %lu blocks for reaching low WM",
00553                       *p_blocks_to_lwm);
00554     }
00555 
00556   return CACHE_CONTENT_SUCCESS;
00557 
00558 }
00559 
00569 int cache_content_local_cache_opendir(char *cache_dir,
00570                                       cache_content_dirinfo_t * pdirectory)
00571 {
00572   pdirectory->level0_dir = NULL;
00573   pdirectory->level1_dir = NULL;
00574   pdirectory->level2_dir = NULL;
00575   pdirectory->level1_cnt = 0;
00576   pdirectory->cookie0 = NULL;
00577   pdirectory->cookie1 = NULL;
00578   pdirectory->cookie2 = NULL;
00579   strcpy(pdirectory->level0_path, "");
00580   strcpy(pdirectory->level1_name, "");
00581   strcpy(pdirectory->level2_name, "");
00582 
00583   /* opens the top level directory */
00584   if((pdirectory->level0_dir = opendir(cache_dir)) == NULL)
00585     {
00586       cache_content_dir_errno = errno;
00587       return FALSE;
00588     }
00589   else
00590     {
00591       cache_content_dir_errno = 0;
00592       strncpy(pdirectory->level0_path, cache_dir, MAXPATHLEN);
00593     }
00594 
00595   pdirectory->level1_cnt = 0;
00596   return TRUE;
00597 }                               /* cache_content_local_cache_opendir */
00598 
00613 cache_content_status_t cache_content_test_cached(cache_entry_t * pentry_inode,
00614                                                  cache_content_client_t * pclient,
00615                                                  fsal_op_context_t * pcontext,
00616                                                  cache_content_status_t * pstatus)
00617 {
00618   char cache_path_index[MAXPATHLEN];
00619 
00620   if(pstatus == NULL)
00621     return CACHE_CONTENT_INVALID_ARGUMENT;
00622 
00623   if(pentry_inode == NULL || pclient == NULL || pcontext == NULL)
00624     {
00625       *pstatus = CACHE_CONTENT_INVALID_ARGUMENT;
00626       return *pstatus;
00627     }
00628 
00629   /* Build the cache index path */
00630   if((*pstatus = cache_content_create_name(cache_path_index,
00631                                            CACHE_CONTENT_INDEX_FILE,
00632                                            pcontext,
00633                                            pentry_inode,
00634                                            pclient)) != CACHE_CONTENT_SUCCESS)
00635     {
00636       return *pstatus;
00637     }
00638 
00639   /* Check if the file exists */
00640   if(access(cache_path_index, F_OK) == 0)
00641     {
00642       /* File is accessible and exists */
00643       *pstatus = CACHE_CONTENT_SUCCESS;
00644       return CACHE_CONTENT_SUCCESS;
00645     }
00646 
00647   /* No access */
00648   *pstatus = CACHE_CONTENT_NOT_FOUND;
00649   return CACHE_CONTENT_NOT_FOUND;
00650 
00651 }                               /* cache_content_test_cached */
00652 
00664 int cache_content_local_cache_dir_iter(cache_content_dirinfo_t * directory,
00665                                        struct dirent *pdir_entry,
00666                                        unsigned int index, unsigned int mod)
00667 {
00668   int rc_readdir = 0;
00669 
00670   /* sanity check */
00671   if(directory == NULL || pdir_entry == NULL)
00672     {
00673       cache_content_dir_errno = EFAULT;
00674       return FALSE;
00675     }
00676 
00677   do
00678     {
00679 
00680       errno = 0;
00681 
00682       /* if the lowest level directory is not opened,
00683        * proceed a readdir, on the topper level directory,
00684        * and so on.
00685        */
00686       if(directory->level2_dir != NULL)
00687         {
00688           rc_readdir =
00689               readdir_r(directory->level2_dir, pdir_entry, &(directory->cookie2));
00690 
00691           if(rc_readdir == 0 && directory->cookie2 != NULL)
00692             {
00693               char d_name_save[MAXNAMLEN];
00694 
00695               /* go to the next loop if the entry is . or .. */
00696               if(!strcmp(".", pdir_entry->d_name) || !strcmp("..", pdir_entry->d_name))
00697                 continue;
00698 
00699               LogFullDebug(COMPONENT_CACHE_CONTENT,"iterator --> %s/%s/%s/%s", directory->level0_path,
00700                      directory->level1_name, directory->level2_name, pdir_entry->d_name);
00701 
00702               /* the d_name must actually be the relative path from
00703                * the cache directory path, so that a file can be
00704                * accessed using <rootpath>/<d_name> path.
00705                */
00706               strncpy(d_name_save, pdir_entry->d_name, MAXNAMLEN);
00707               snprintf(pdir_entry->d_name, MAXNAMLEN, "%s/%s/%s",
00708                        directory->level1_name, directory->level2_name, d_name_save);
00709 
00710               return TRUE;
00711             }
00712           else
00713             {
00714               /* test if it is an error or an end of dir */
00715               if(errno != 0)
00716                 {
00717                   cache_content_dir_errno = errno;
00718                   return TRUE;
00719                 }
00720               else
00721                 {
00722                   /* the lowest level entry dir is finished,
00723                    * must proceed a readdir on the topper level
00724                    */
00725                   closedir(directory->level2_dir);
00726                   directory->level2_dir = NULL;
00727                   /* go to next loop */
00728                 }
00729             }
00730         }
00731       /* continue directory at level 1 */
00732       else if(directory->level1_dir != NULL)
00733         {
00734           if(mod <= 1)
00735             {
00736               /* list all dirs */
00737               rc_readdir =
00738                   readdir_r(directory->level1_dir, pdir_entry, &(directory->cookie1));
00739               directory->level1_cnt += 1;
00740             }
00741           else
00742             {
00743               rc_readdir =
00744                   readdir_r(directory->level1_dir, pdir_entry, &(directory->cookie1));
00745               directory->level1_cnt++;
00746 
00747               LogFullDebug(COMPONENT_CACHE_CONTENT,
00748                   "---> directory->level1_cnt=%u mod=%u index=%u modulocalcule=%u name=%s",
00749                    directory->level1_cnt, mod, index, directory->level1_cnt % mod,
00750                    pdir_entry->d_name);
00751 
00752               /* skip entry if  cnt % mod == index */
00753               if((directory->level1_cnt % mod != index))
00754                 continue;
00755             }
00756 
00757           if(rc_readdir == 0 && directory->cookie1 != NULL)
00758             {
00759               char dirpath[MAXPATHLEN];
00760 
00761               /* go to the next loop if this is the . or .. entry */
00762               if(!strcmp(".", pdir_entry->d_name) || !strcmp("..", pdir_entry->d_name))
00763                 continue;
00764 
00765               strncpy(directory->level2_name, pdir_entry->d_name, MAXNAMLEN);
00766 
00767               /* must now open the entry as the level2 directory */
00768               snprintf(dirpath, MAXPATHLEN, "%s/%s/%s",
00769                        directory->level0_path,
00770                        directory->level1_name, directory->level2_name);
00771 
00772               if((directory->level2_dir = opendir(dirpath)) == NULL)
00773                 {
00774                   cache_content_dir_errno = errno;
00775                   return FALSE;
00776                 }
00777             }
00778           else
00779             {
00780               /* test if it is an error or an end of dir */
00781               if(errno != 0)
00782                 {
00783                   cache_content_dir_errno = errno;
00784                   return TRUE;
00785                 }
00786               else
00787                 {
00788                   /* the lowest level entry dir is finished,
00789                    * must proceed a readdir on the topper level
00790                    */
00791                   closedir(directory->level1_dir);
00792                   directory->level1_dir = NULL;
00793                 }
00794             }
00795         }
00796       /* continue directory at level 0 */
00797       else if(directory->level0_dir != NULL)
00798         {
00799 
00800           rc_readdir =
00801               readdir_r(directory->level0_dir, pdir_entry, &(directory->cookie0));
00802 
00803           if(rc_readdir == 0 && directory->cookie0 != NULL)
00804             {
00805               char dirpath[MAXPATHLEN];
00806 
00807               /* go to the next loop if this is the . or .. entry */
00808               if(!strcmp(".", pdir_entry->d_name) || !strcmp("..", pdir_entry->d_name))
00809                 continue;
00810 
00811               strncpy(directory->level1_name, pdir_entry->d_name, MAXNAMLEN);
00812 
00813               /* must now open the entry as the level1 directory */
00814               snprintf(dirpath, MAXPATHLEN, "%s/%s",
00815                        directory->level0_path, directory->level1_name);
00816 
00817               directory->level1_cnt = 0;
00818 
00819               if((directory->level1_dir = opendir(dirpath)) == NULL)
00820                 {
00821                   cache_content_dir_errno = errno;
00822                   return TRUE;
00823                 }
00824             }
00825           else
00826             {
00827               /* test if it is an error or an end of dir */
00828               if(errno != 0)
00829                 {
00830                   cache_content_dir_errno = errno;
00831                   return TRUE;
00832                 }
00833               else
00834                 {
00835                   /* we are at the end of the level directory
00836                    * return End of Dir
00837                    */
00838                   cache_content_dir_errno = 0;
00839                   return FALSE;
00840                 }
00841             }
00842         }
00843       else
00844         {
00845           /* invalid base directory descriptor */
00846           cache_content_dir_errno = EINVAL;
00847           return TRUE;
00848         }
00849 
00850     }
00851   while(1);
00852 
00853   cache_content_dir_errno = -1;
00854   /* should never happen */
00855   return TRUE;
00856 
00857 }                               /* cache_content_local_cache_dir_iter */
00858 
00868 void cache_content_local_cache_closedir(cache_content_dirinfo_t * directory)
00869 {
00870   if(directory != NULL)
00871     {
00872       if(directory->level2_dir != NULL)
00873         {
00874           closedir(directory->level2_dir);
00875           directory->level2_dir = NULL;
00876         }
00877 
00878       if(directory->level1_dir != NULL)
00879         {
00880           closedir(directory->level1_dir);
00881           directory->level1_dir = NULL;
00882         }
00883 
00884       if(directory->level0_dir != NULL)
00885         {
00886           closedir(directory->level0_dir);
00887           directory->level0_dir = NULL;
00888         }
00889     }
00890 }                               /* cache_content_local_cache_closedir */