Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright 2006 Richard Wilson <info@tinct.net>
  3.  *
  4.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  5.  *
  6.  * NetSurf 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; version 2 of the License.
  9.  *
  10.  * NetSurf is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17.  */
  18.  
  19. /** \file
  20.  * Provides a central method of obtaining unique filenames.
  21.  *
  22.  * A maximum of 2^24 files can be allocated at any point in time.
  23.  */
  24.  
  25. #include <assert.h>
  26. #include <sys/types.h>
  27. #include <dirent.h>
  28. #include <stdbool.h>
  29. #include <string.h>
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <errno.h>
  33. #include <sys/stat.h>
  34. #include <unistd.h>
  35.  
  36. #include "utils/config.h"
  37. #include "utils/filename.h"
  38. #include "utils/log.h"
  39. #include "utils/utils.h"
  40.  
  41. #define FULL_WORD (unsigned int)0xffffffffu
  42. #define START_PREFIX ('0' + '0' * 10)
  43.  
  44. struct directory {
  45.         int numeric_prefix;             /** numeric representation of prefix */
  46.         char prefix[10];                /** directory prefix, eg '00/11/52/' */
  47.         unsigned int low_used;          /** first 32 files, 1 bit per file */
  48.         unsigned int high_used;         /** last 32 files, 1 bit per file */
  49.         struct directory *next;         /** next directory (sorted by prefix) */
  50. };
  51.  
  52.  
  53. static struct directory *root = NULL;
  54. static char filename_buffer[12];
  55. static char filename_directory[256];
  56.  
  57. static struct directory *filename_create_directory(const char *prefix);
  58. static bool filename_flush_directory(const char *folder, int depth);
  59. static bool filename_delete_recursive(char *folder);
  60.  
  61. /**
  62.  * Request a new, unique, filename.
  63.  *
  64.  * \return a pointer to a shared buffer containing the new filename,
  65.  *         NULL on failure
  66.  */
  67. const char *filename_request(void)
  68. {
  69.         struct directory *dir;
  70.         int i = -1;
  71.  
  72.         for (dir = root; dir; dir = dir->next) {
  73.                 if ((dir->low_used & dir->high_used) != FULL_WORD) {
  74.                         if (dir->low_used != FULL_WORD) {
  75.                                 for (i = 0; (dir->low_used & (1 << i)); i++);
  76.                         } else {
  77.                                 for (i = 0; (dir->high_used & (1 << i)); i++);
  78.                                 i += 32;
  79.                         }
  80.                         break;
  81.                 }
  82.         }
  83.  
  84.         if (i == -1) {
  85.                 /* no available slots - create a new directory */
  86.                 dir = filename_create_directory(NULL);
  87.                 if (dir == NULL) {
  88.                         LOG(("Failed to create a new directory."));
  89.                         return NULL;
  90.                 }
  91.                 i = 63;
  92.         }
  93.  
  94.         if (i < 32)
  95.                 dir->low_used |= (1 << i);
  96.         else
  97.                 dir->high_used |= (1 << (i - 32));
  98.  
  99.         sprintf(filename_buffer, "%s%.2i", dir->prefix, i);
  100.  
  101.         return filename_buffer;
  102. }
  103.  
  104.  
  105. /**
  106.  * Claim a specific filename.
  107.  *
  108.  * \param  filename  the filename to claim
  109.  * \return whether the claim was successful
  110.  */
  111. bool filename_claim(const char *filename)
  112. {
  113.         char dir_prefix[9];
  114.         int file;
  115.         struct directory *dir;
  116.  
  117.         /* filename format is always '01/23/45/XX' */
  118.         strncpy(dir_prefix, filename, 9);
  119.         dir_prefix[8] = '\0';
  120.         file = (filename[10] + filename[9] * 10 - START_PREFIX);
  121.  
  122.         /* create the directory */
  123.         dir = filename_create_directory(dir_prefix);
  124.         if (dir == NULL)
  125.                 return false;
  126.  
  127.         /* update the entry */
  128.         if (file < 32) {
  129.                 if (dir->low_used & (1 << file))
  130.                         return false;
  131.                 dir->low_used |= (1 << file);
  132.         } else {
  133.                 if (dir->high_used & (1 << (file - 32)))
  134.                         return false;
  135.                 dir->high_used |= (1 << (file - 32));
  136.         }
  137.  
  138.         return true;
  139. }
  140.  
  141.  
  142. /**
  143.  * Releases a filename for future use.
  144.  *
  145.  * \param  filename  the filename to release
  146.  */
  147. void filename_release(const char *filename)
  148. {
  149.         struct directory *dir;
  150.         int index, file;
  151.  
  152.         /* filename format is always '01/23/45/XX' */
  153.         index = ((filename[7] + filename[6] * 10 - START_PREFIX) |
  154.                 ((filename[4] + filename[3] * 10 - START_PREFIX) << 6) |
  155.                 ((filename[1] + filename[0] * 10 - START_PREFIX) << 12));
  156.         file = (filename[10] + filename[9] * 10 - START_PREFIX);
  157.  
  158.         /* modify the correct directory entry */
  159.         for (dir = root; dir; dir = dir->next) {
  160.                 if (dir->numeric_prefix == index) {
  161.                         if (file < 32)
  162.                                 dir->low_used &= ~(1 << file);
  163.                         else
  164.                                 dir->high_used &= ~(1 << (file - 32));
  165.                         return;
  166.                 }
  167.         }
  168. }
  169.  
  170.  
  171. /**
  172.  * Initialise the filename provider.
  173.  */
  174. bool filename_initialise(void)
  175. {
  176.         char *directory, *start;
  177.  
  178.         directory = strdup(TEMP_FILENAME_PREFIX);
  179.         if (directory == NULL)
  180.                 return false;
  181.  
  182.         for (start = directory; *start != '\0'; start++) {
  183.                 if (*start == '/') {
  184.                         *start = '\0';
  185.                         nsmkdir(directory, S_IRWXU);
  186.                         *start = '/';
  187.                 }
  188.         }
  189.  
  190.         LOG(("Temporary directory location: %s", directory));
  191.         nsmkdir(directory, S_IRWXU);
  192.  
  193.         free(directory);
  194.  
  195.         return true;
  196. }
  197.  
  198.  
  199. /**
  200.  * Deletes all files in the cache directory that are not accounted for.
  201.  */
  202. void filename_flush(void)
  203. {
  204.         while (filename_flush_directory(TEMP_FILENAME_PREFIX, 0));
  205. }
  206.  
  207.  
  208. /**
  209.  * Deletes some files in a directory that are not accounted for.
  210.  *
  211.  * A single call to this function may not delete all the files in
  212.  * a directory. It should be called until it returns false.
  213.  *
  214.  * \param folder        the folder to search
  215.  * \param depth         the folder depth
  216.  * \returns whether further calls may be needed
  217.  */
  218. bool filename_flush_directory(const char *folder, int depth)
  219. {
  220.         DIR *parent;
  221.         struct dirent *entry;
  222.         bool changed = false;
  223.         bool del;
  224.         int number, i;
  225.         int prefix = 0;
  226.         unsigned int prefix_mask = (0x3f << 12);
  227.         char child[256];
  228.         const char *prefix_start = NULL;
  229.         struct directory *dir = NULL;
  230.  
  231.         /* Maximum permissible depth is 3 */
  232.         assert(depth <= 3);
  233.  
  234.         if (depth > 0) {
  235.                 /* Not a top-level directory, so determine the prefix
  236.                  * by removing the last /XX component */
  237.                 prefix_start = folder + strlen(folder) - depth * 3 + 1;
  238.         }
  239.  
  240.         /* Calculate the numeric prefix */
  241.         for (i = 0; i < depth; i++) {
  242.                 number = prefix_start[1] + prefix_start[0] * 10 - START_PREFIX;
  243.                 prefix |= (number << (12 - i * 6));
  244.                 prefix_mask |= (0x3f << (12 - i * 6));
  245.                 prefix_start += 3;
  246.         }
  247.  
  248.         /* If we're flushing a leaf directory, find it in the list */
  249.         if (depth == 3) {
  250.                 for (dir = root; dir; dir = dir->next) {
  251.                         if (dir->numeric_prefix == prefix)
  252.                                 break;
  253.                 }
  254.  
  255.                 if (dir == NULL)
  256.                         return false;
  257.         }
  258.  
  259.         parent = opendir(folder);
  260.  
  261.         while ((entry = readdir(parent))) {
  262.                 struct stat statbuf;
  263.  
  264.                 /* Ignore '.' and '..' */
  265.                 if (strcmp(entry->d_name, ".") == 0 ||
  266.                                 strcmp(entry->d_name, "..") == 0)
  267.                         continue;
  268.  
  269.                 snprintf(child, sizeof(child), "%s/%s", folder, entry->d_name);
  270.                 child[sizeof(child) - 1] = '\0';
  271.  
  272.                 if (stat(child, &statbuf) == -1) {
  273.                         LOG(("Unable to stat %s: %s", child, strerror(errno)));
  274.                         continue;
  275.                 }
  276.  
  277.                 /* first 3 depths are directories only, then files only */
  278.                 if (depth < 3) {
  279.                         /* Delete any unexpected files */
  280.                         del = !S_ISDIR(statbuf.st_mode);
  281.                 } else {
  282.                         /* Delete any unexpected directories */
  283.                         del = S_ISDIR(statbuf.st_mode);
  284.                 }
  285.  
  286.                 /* check we are a file numbered '00' -> '63' */
  287.                 if (del == false && (entry->d_name[0] >= '0') &&
  288.                                 (entry->d_name[0] <= '6') &&
  289.                                 (entry->d_name[1] >= '0') &&
  290.                                 (entry->d_name[1] <= '9') &&
  291.                                 (entry->d_name[2] == '\0')) {
  292.                         number = atoi(entry->d_name);
  293.  
  294.                         if (number >= 0 && number <= 63) {
  295.                                 if (depth == 3) {
  296.                                         /* File: delete if not in bitfield */
  297.                                         if (number < 32)
  298.                                                 del = !(dir->low_used &
  299.                                                         (1 << number));
  300.                                         else
  301.                                                 del = !(dir->high_used &
  302.                                                         (1 << (number - 32)));
  303.                                 } else {
  304.                                         /* Directory: delete unless in list */
  305.                                         del = true;
  306.  
  307.                                         /* Insert into numeric prefix */
  308.                                         prefix &= ~(0x3f << (12 - depth * 6));
  309.                                         prefix |= (number << (12 - depth * 6));
  310.  
  311.                                         /* Find in dir list */
  312.                                         for (dir = root; dir; dir = dir->next) {
  313.                                                 number = dir->numeric_prefix &
  314.                                                                 prefix_mask;
  315.                                                 if (number == prefix) {
  316.                                                         /* In list: retain */
  317.                                                         del = false;
  318.                                                         break;
  319.                                                 }
  320.                                         }
  321.                                 }
  322.                         } else {
  323.                                 /* Unexpected name: delete */
  324.                                 del = true;
  325.                         }
  326.                 } else {
  327.                         /* Unexpected name: delete */
  328.                         del = true;
  329.                 }
  330.  
  331.                 /* continue if this is a file we want to retain */
  332.                 if (del == false && (!S_ISDIR(statbuf.st_mode)))
  333.                         continue;
  334.  
  335.                 /* delete or recurse */
  336.                 if (del) {
  337.                         if (S_ISDIR(statbuf.st_mode))
  338.                                 filename_delete_recursive(child);
  339.  
  340.                         if (remove(child))
  341.                                 LOG(("Failed to remove '%s'", child));
  342.                         else
  343.                                 changed = true;
  344.                 } else {
  345.                         while (filename_flush_directory(child, depth + 1));
  346.                 }
  347.         }
  348.  
  349.         closedir(parent);
  350.  
  351.         return changed;
  352. }
  353.  
  354.  
  355. /**
  356.  * Recursively deletes the contents of a directory
  357.  *
  358.  * \param directory     the directory to delete
  359.  * \return true on success, false otherwise
  360.  */
  361. bool filename_delete_recursive(char *folder)
  362. {
  363.         DIR *parent;
  364.         struct dirent *entry;
  365.         char child[256];
  366.         struct stat statbuf;
  367.  
  368.         parent = opendir(folder);
  369.  
  370.         while ((entry = readdir(parent))) {
  371.                 /* Ignore '.' and '..' */
  372.                 if (strcmp(entry->d_name, ".") == 0 ||
  373.                                 strcmp(entry->d_name, "..") == 0)
  374.                         continue;
  375.  
  376.                 snprintf(child, sizeof(child), "%s/%s", folder, entry->d_name);
  377.                 child[sizeof(child) - 1] = '\0';
  378.  
  379.                 if (stat(child, &statbuf) == -1) {
  380.                         LOG(("Unable to stat %s: %s", child, strerror(errno)));
  381.                         continue;
  382.                 }
  383.  
  384.                 if (S_ISDIR(statbuf.st_mode)) {
  385.                         if (!filename_delete_recursive(child)) {
  386.                                 closedir(parent);
  387.                                 return false;
  388.                         }
  389.                 }
  390.  
  391.                 if (remove(child)) {
  392.                         LOG(("Failed to remove '%s'", child));
  393.                         closedir(parent);
  394.                         return false;
  395.                 }
  396.         }
  397.  
  398.         closedir(parent);
  399.  
  400.         return true;
  401. }
  402.  
  403.  
  404. /**
  405.  * Creates a new directory.
  406.  *
  407.  * \param  prefix  the prefix to use, or NULL to allocate a new one
  408.  * \return a new directory structure, or NULL on memory exhaustion or
  409.  * creation failure
  410.  *
  411.  * Empty directories are never deleted, except by an explicit call to
  412.  * filename_flush().
  413.  */
  414. static struct directory *filename_create_directory(const char *prefix)
  415. {
  416.         char *last_1, *last_2;
  417.         int index;
  418.         struct directory *old_dir, *new_dir, *prev_dir = NULL;
  419.         char dir_prefix[16];
  420.         int i;
  421.  
  422.         /* get the lowest unique prefix, or use the provided one */
  423.         if (prefix == NULL) {
  424.                 for (index = 0, old_dir = root; old_dir;
  425.                                 index++, old_dir = old_dir->next) {
  426.                         if (old_dir->numeric_prefix != index)
  427.                                 break;
  428.  
  429.                         prev_dir = old_dir;
  430.                 }
  431.  
  432.                 sprintf(dir_prefix, "%.2i/%.2i/%.2i/",
  433.                                 ((index >> 12) & 63),
  434.                                 ((index >> 6) & 63),
  435.                                 ((index >> 0) & 63));
  436.  
  437.                 prefix = dir_prefix;
  438.         } else {
  439.                 /* prefix format is always '01/23/45/' */
  440.                 index = ((prefix[7] + prefix[6] * 10 - START_PREFIX) |
  441.                         ((prefix[4] + prefix[3] * 10 - START_PREFIX) << 6) |
  442.                         ((prefix[1] + prefix[0] * 10 - START_PREFIX) << 12));
  443.  
  444.                 for (old_dir = root; old_dir; old_dir = old_dir->next) {
  445.                         if (old_dir->numeric_prefix == index)
  446.                                 return old_dir;
  447.  
  448.                         else if (old_dir->numeric_prefix > index)
  449.                                 break;
  450.  
  451.                         prev_dir = old_dir;
  452.                 }
  453.         }
  454.  
  455.         /* allocate a new directory */
  456.         new_dir = malloc(sizeof(struct directory));
  457.         if (new_dir == NULL) {
  458.                 LOG(("No memory for malloc()"));
  459.                 return NULL;
  460.         }
  461.  
  462.         strncpy(new_dir->prefix, prefix, 9);
  463.         new_dir->prefix[9] = '\0';
  464.         new_dir->low_used = new_dir->high_used = 0;
  465.         new_dir->numeric_prefix = index;
  466.  
  467.         if (prev_dir == NULL) {
  468.                 new_dir->next = root;
  469.                 root = new_dir;
  470.         } else {
  471.                 new_dir->next = prev_dir->next;
  472.                 prev_dir->next = new_dir;
  473.         }
  474.  
  475.         /* if the previous directory has the same parent then we can simply
  476.          * create the child. */
  477.         if (prev_dir && strncmp(prev_dir->prefix, new_dir->prefix, 6) == 0) {
  478.                 new_dir->prefix[8] = '\0';
  479.                 sprintf(filename_directory, "%s/%s",
  480.                                 TEMP_FILENAME_PREFIX,
  481.                                 new_dir->prefix);
  482.                 new_dir->prefix[8] = '/';
  483.  
  484.                 if (!is_dir(filename_directory)) {
  485.                         if (!nsmkdir(filename_directory, S_IRWXU))
  486.                                 return new_dir;
  487.  
  488.                         /* the user has probably deleted the parent directory
  489.                          * whilst we are running if there is an error, so we
  490.                          * don't report this yet and try to create the
  491.                          * structure normally. */
  492.                         LOG(("Failed to create optimised structure '%s'",
  493.                                         filename_directory));
  494.                 }
  495.         }
  496.  
  497.         /* create the directory structure */
  498.         sprintf(filename_directory, "%s/", TEMP_FILENAME_PREFIX);
  499.         last_1 = filename_directory + SLEN(TEMP_FILENAME_PREFIX) + 1;
  500.         last_2 = new_dir->prefix;
  501.  
  502.         /* create each subdirectory, up to the maximum depth of 3 */
  503.         for (i = 0; i < 3 && *last_2; i++) {
  504.                 *last_1++ = *last_2++;
  505.                 while (*last_2 && *last_2 != '/')
  506.                         *last_1++ = *last_2++;
  507.  
  508.                 if (*last_2) {
  509.                         last_1[0] = '\0';
  510.  
  511.                         if (!is_dir(filename_directory)) {
  512.                                 if (nsmkdir(filename_directory, S_IRWXU)) {
  513.                                         LOG(("Failed to create directory '%s'",
  514.                                                         filename_directory));
  515.                                         return NULL;
  516.                                 }
  517.                         }
  518.                 }
  519.         }
  520.  
  521.         return new_dir;
  522. }
  523.