Branch: Tag:

1999-05-06

1999-05-06 20:53:17 by Henrik Grubbström (Grubba) <grubba@grubba.org>

Some quota support.

Rev: server/etc/modules/Roxen.pmod:1.1

1: + /* +  * $Id: Roxen.pmod,v 1.1 1999/05/06 20:53:17 grubba Exp $ +  * +  * Various helper functions. +  * +  * Henrik Grubbström 1999-05-03 +  */    -  + /* +  * TODO: +  * +  * o Quota: Fix support for the index file. +  * +  */ +  + #ifdef QUOTA_DEBUG + #define QD_WRITE(X) werror(X) + #else /* !QUOTA_DEBUG */ + #define QD_WRITE(X) + #endif /* QUOTA_DEBUG */ +  + class QuotaDB + { + #if constant(create_thread) +  object(Thread.Mutex) lock = Thread.Mutex(); + #define LOCK() mixed key__; catch { _key = lock->lock(); } + #define UNLOCK() do { if (key__) destruct(key__); } while(0) + #else /* !constant(create_thread) */ + #define LOCK() + #define UNLOCK() + #endif /* constant(create_thread) */ +  +  constant READ_BUF_SIZE = 256; +  constant CACHE_SIZE_LIMIT = 512; +  +  object index_file; +  object catalog_file; +  object data_file; +  +  mapping(string:int) new_entries_cache = ([]); +  mapping(string:object) active_objects = ([]); +  +  array(int) index; +  +  int next_offset; +  +  static class QuotaEntry +  { +  string name; +  int data_offset; +  +  static int quota; +  +  static void store() +  { +  LOCK(); +  +  QD_WRITE(sprintf("QuotaEntry::store(): Quota for %O is now %O\n", +  name, quota)); +  +  data_file->seek(data_offset); +  data_file->write(sprintf("%4c", quota)); +  +  UNLOCK(); +  } +  +  static void read() +  { +  LOCK(); +  +  data_file->seek(data_offset); +  string s = data_file->read(4); +  +  quota = 0; +  sscanf(s, "%4c", quota); +  +  QD_WRITE(sprintf("QuotaEntry::read(): Quota for %O is %O\n", +  name, quota)); +  +  UNLOCK(); +  } +  +  void create(string n, int d_o) +  { +  QD_WRITE(sprintf("QuotaEntry(%O, %O)\n", n, d_o)); +  +  name = n; +  data_offset = d_o; +  +  read(); +  } +  +  int check_quota(string uri, int amount) +  { +  QD_WRITE(sprintf("QuotaEntry::check_quota(%O, %O): quota:%d\n", +  uri, amount, quota)); +  +  if (amount == 0x7fffffff) { +  // Workaround for FTP. +  return 1; +  } +  +  return(amount <= quota); +  } +  +  int allocate(string uri, int amount) +  { +  QD_WRITE(sprintf("QuotaEntry::allocate(%O, %O): quota:%d => %d\n", +  uri, amount, quota, quota - amount)); +  +  quota -= amount; +  +  store(); +  +  return(quota >= 0); +  } +  +  int deallocate(string uri, int amount) +  { +  return(allocate(uri, -amount)); +  } +  +  int get_quota(string uri) +  { +  return quota; +  } +  +  void set_quota(string uri, int amount) +  { +  quota = amount; +  +  store(); +  } +  + #if !constant(set_weak_flag) +  static int refs; +  +  void add_ref() +  { +  refs++; +  } +  +  void free_ref() +  { +  if (!(--refs)) { +  destruct(); +  } +  } +  } +  +  static class QuotaProxy +  { +  static object(QuotaEntry) master; +  +  function(string, int:int) check_quota; +  function(string, int:int) allocate; +  function(string, int:int) deallocate; +  function(string, int:void) set_quota; +  function(string:int) get_quota; +  +  void create(object(QuotaEntry) m) +  { +  master = m; +  master->add_ref(); +  check_quota = master->check_quota; +  allocate = master->allocate; +  deallocate = master->deallocate; +  set_quota = master->set_quota; +  get_quota = master->get_quota; +  } +  +  void destroy() +  { +  master->free_ref(); +  } + #endif /* !constant(set_weak_flag) */ +  } +  +  static object read_entry(int offset) +  { +  QD_WRITE(sprintf("QuotaDB::read_entry(%O)\n", offset)); +  +  catalog_file->seek(offset); +  +  string data = catalog_file->read(READ_BUF_SIZE); +  +  if (data == "") { +  QD_WRITE(sprintf("QuotaDB::read_entry(%O): At EOF\n", offset)); +  +  return 0; +  } +  +  int len; +  int data_offset; +  string key; +  +  sscanf(data[..7], "%4c%4c", len, data_offset); +  if (len > sizeof(data)) { +  key = data[8..] + catalog_file->read(len - sizeof(data)); +  +  len -= 8; +  +  if (sizeof(key) != len) { +  error(sprintf("Failed to read catalog entry at offset %d.\n" +  "len: %d, sizeof(key):%d\n", +  offset, len, sizeof(key))); +  } +  } else { +  key = data[8..len-1]; +  } +  +  return QuotaEntry(key, data_offset); +  } +  +  static object open(string fname, int|void create_new) +  { +  object f = Stdio.File(); +  string mode = create_new?"rwc":"rw"; +  +  if (!f->open(fname, mode)) { +  error(sprintf("Failed to open quota file %O.\n", fname)); +  } +  if (f->try_lock && !f->try_lock()) { +  error(sprintf("Failed to lock quota file %O.\n", fname)); +  } +  return(f); +  } +  +  static void rebuild_index() +  { +  // FIXME: Actually make an index file. +  } +  +  static object low_lookup(string key) +  { +  QD_WRITE(sprintf("QuotaDB::low_lookup(%O)\n", key)); +  +  int data_offset; +  +  if (zero_type(data_offset = new_entries_cache[key])) { +  // FIXME: Try the index file. +  +  QD_WRITE(sprintf("QuotaDB::low_lookup(%O): Not found\n", key)); +  +  return 0; +  } +  + #if constant(set_weak_flag) +  return active_objects[key] = read_entry(data_offset); + #else /* !constant(set_weak_flag) */ +  return QuotaProxy(active_objects[key] = read_entry(data_offset)); + #endif /* constant(set_weak_flag) */ +  } +  +  object lookup(string key, int quota) +  { +  QD_WRITE(sprintf("QuotaDB::lookup(%O, %O)\n", key, quota)); +  +  LOCK(); +  +  object res; +  +  if (res = active_objects[key]) { +  QD_WRITE(sprintf("QuotaDB::lookup(%O, %O): User in active objects.\n", +  key, quota)); +  + #if constant(set_weak_flag) +  return res; + #else /* !constant(set_weak_flag) */ +  return QuotaProxy(res); + #endif /* constant(set_weak_flag) */ +  } +  if (res = low_lookup(key)) { +  return res; +  } +  +  QD_WRITE(sprintf("QuotaDB::lookup(%O, %O): New user.\n", key, quota)); +  +  // Search to EOF. +  data_file->seek(-1); +  data_file->read(1); +  +  catalog_file->seek(next_offset); +  +  // We should now be at EOF. +  +  int data_offset = data_file->tell(); +  +  // Initialize. +  if (data_file->write(sprintf("%4c", quota)) != 4) { +  error(sprintf("write() failed for quota data file!\n")); +  } +  string entry = sprintf("%4c%4c%s", sizeof(key)+8, data_offset, key); +  +  if (catalog_file->write(entry) != sizeof(entry)) { +  error(sprintf("write() failed for quota catalog file!\n")); +  } +  +  new_entries_cache[key] = next_offset; +  next_offset = catalog_file->tell(); +  +  if (sizeof(new_entries_cache) > CACHE_SIZE_LIMIT) { +  rebuild_index(); +  } +  +  // low_lookup will always succeed at this point. +  return low_lookup(key); +  } +  +  void create(string base_name, int|void create_new) +  { +  index_file = open(base_name + ".index", create_new); +  catalog_file = open(base_name + ".cat", create_new); +  data_file = open(base_name + ".data", create_new); +  + #if constant(set_weak_flag) +  set_weak_flag(active_objects, 1); + #endif /* constant(set_weak_flag) */ +  +  /* Initialize the new_entries table. */ +  array index_st = index_file->stat(); +  if (!index_st || !sizeof(index_st)) { +  error(sprintf("stat() failed for quota index file!\n")); +  } +  array data_st = data_file->stat(); +  if (!data_st || !sizeof(data_st)) { +  error(sprintf("stat() failed for quota data file!\n")); +  } +  if (index_st[1] < 0) { +  error("quota index file isn't a regular file!\n"); +  } +  if (data_st[1] < 0) { +  error("quota data file isn't a regular file!\n"); +  } +  if (data_st[1] < index_st[1]) { +  error("quota data file is shorter than the index file!\n"); +  } +  if (index_st[1] & 3) { +  error("quota index file has odd length!\n"); +  } +  if (data_st[1] & 3) { +  error("quota data file has odd length!\n"); +  } +  +  /* Read the index, and find the last entry in the catalog file. +  */ +  int i; +  array(string) index_str = index_file->read()/4; +  index = allocate(sizeof(index_str)); +  +  if (sizeof(index)*4 != sizeof(index_str)) { +  error("Truncated read of the index file!\n"); +  } +  +  foreach(index_str, string offset_str) { +  int offset; +  sscanf(offset_str, "%4c", offset); +  index[i++] = offset; +  if (offset > next_offset) { +  next_offset = offset; +  } +  } +  +  if (sizeof(index)) { +  /* Skip past the last entry in the catalog file */ +  mixed entry = read_entry(next_offset); +  next_offset = catalog_file->tell(); +  } +  +  if (index_st[1] < data_st[1]) { +  /* Put everything else in the new_entries_cache */ +  while (mixed entry = read_entry(next_offset)) { +  new_entries_cache[entry->name] = next_offset; +  next_offset = catalog_file->tell(); +  } +  +  /* Clean up the index. */ +  rebuild_index(); +  } +  } + }   Newline at end of file added.