Roxen.git/
server/
etc/
modules/
Roxen.pmod
Branch:
Tag:
Non-build tags
All tags
No tags
1999-05-06
1999-05-06 20:53:17 by Henrik Grubbström (Grubba) <grubba@grubba.org>
ebc988334e85342eb5e49f9531c88a4473a49d1c (
381
lines) (+
381
/-
0
)
[
Show
|
Annotate
]
Branch:
5.2
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.