~ubuntu-branches/ubuntu/wily/ruby-ferret/wily

« back to all changes in this revision

Viewing changes to ext/fs_store.c

  • Committer: Bazaar Package Importer
  • Author(s): Antonio Terceiro
  • Date: 2011-07-28 00:02:49 UTC
  • Revision ID: james.westby@ubuntu.com-20110728000249-v0443y69ftcpxwi6
Tags: upstream-0.11.6
ImportĀ upstreamĀ versionĀ 0.11.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include "store.h"
 
2
#include <time.h>
 
3
#include <sys/types.h>
 
4
#include <fcntl.h>
 
5
#include <sys/stat.h>
 
6
#include <errno.h>
 
7
#include <string.h>
 
8
#include <stdio.h>
 
9
#ifdef POSH_OS_WIN32
 
10
# include <io.h>
 
11
# include "win32.h"
 
12
# ifndef sleep
 
13
#   define sleep _sleep
 
14
# endif
 
15
# ifndef DIR_SEPARATOR
 
16
#   define DIR_SEPARATOR "\\"
 
17
# endif
 
18
# ifndef S_IRUSR
 
19
#   define S_IRUSR _S_IREAD
 
20
# endif
 
21
# ifndef S_IWUSR
 
22
#   define S_IWUSR _S_IWRITE
 
23
# endif
 
24
#else
 
25
# define DIR_SEPARATOR "/"
 
26
# include <unistd.h>
 
27
# include <dirent.h>
 
28
#endif
 
29
#ifndef O_BINARY
 
30
# define O_BINARY 0
 
31
#endif
 
32
 
 
33
extern Store *store_new();
 
34
extern void store_destroy(Store *store);
 
35
extern OutStream *os_new();
 
36
extern InStream *is_new();
 
37
extern int file_is_lock(char *filename);
 
38
 
 
39
/**
 
40
 * Create a filepath for a file in the store using the operating systems
 
41
 * default file seperator.
 
42
 */
 
43
static char *join_path(char *buf, const char *base, const char *filename)
 
44
{
 
45
  snprintf(buf, MAX_FILE_PATH, "%s"DIR_SEPARATOR"%s", base, filename);
 
46
  return buf;
 
47
}
 
48
 
 
49
static void fs_touch(Store *store, char *filename)
 
50
{
 
51
    int f;
 
52
    char path[MAX_FILE_PATH];
 
53
    join_path(path, store->dir.path, filename);
 
54
    if ((f = creat(path, store->file_mode)) == 0) {
 
55
        RAISE(IO_ERROR, "couldn't create file %s: <%s>", path,
 
56
              strerror(errno));
 
57
    }
 
58
    close(f);
 
59
}
 
60
 
 
61
static int fs_exists(Store *store, char *filename)
 
62
{
 
63
    int fd;
 
64
    char path[MAX_FILE_PATH];
 
65
    join_path(path, store->dir.path, filename);
 
66
    fd = open(path, 0);
 
67
    if (fd < 0) {
 
68
        if (errno != ENOENT) {
 
69
            RAISE(IO_ERROR, "checking existance of %s: <%s>", path,
 
70
                  strerror(errno));
 
71
        }
 
72
        return false;
 
73
    }
 
74
    close(fd);
 
75
    return true;
 
76
}
 
77
 
 
78
static int fs_remove(Store *store, char *filename)
 
79
{
 
80
    char path[MAX_FILE_PATH];
 
81
    return remove(join_path(path, store->dir.path, filename));
 
82
}
 
83
 
 
84
static void fs_rename(Store *store, char *from, char *to)
 
85
{
 
86
    char path1[MAX_FILE_PATH], path2[MAX_FILE_PATH];
 
87
 
 
88
#ifdef POSH_OS_WIN32
 
89
    remove(join_path(path1, store->dir.path, to));
 
90
#endif
 
91
 
 
92
    if (rename(join_path(path1, store->dir.path, from),
 
93
               join_path(path2, store->dir.path, to)) < 0) {
 
94
        RAISE(IO_ERROR, "couldn't rename file \"%s\" to \"%s\": <%s>",
 
95
              path1, path2, strerror(errno));
 
96
    }
 
97
}
 
98
 
 
99
static int fs_count(Store *store)
 
100
{
 
101
    int cnt = 0;
 
102
    struct dirent *de;
 
103
    DIR *d = opendir(store->dir.path);
 
104
 
 
105
    if (!d) {
 
106
        RAISE(IO_ERROR, "counting files in %s: <%s>",
 
107
              store->dir.path, strerror(errno));
 
108
    }
 
109
 
 
110
    while ((de = readdir(d)) != NULL) {
 
111
        if (de->d_name[0] > '/') { /* skip ., .., / and '\0'*/
 
112
            cnt++;
 
113
        }
 
114
    }
 
115
    closedir(d);
 
116
 
 
117
    return cnt;
 
118
}
 
119
 
 
120
static void fs_each(Store *store, void (*func)(char *fname, void *arg), void *arg)
 
121
{
 
122
    struct dirent *de;
 
123
    DIR *d = opendir(store->dir.path);
 
124
 
 
125
    if (!d) {
 
126
        RAISE(IO_ERROR, "doing 'each' in %s: <%s>",
 
127
              store->dir.path, strerror(errno));
 
128
    }
 
129
 
 
130
    while ((de = readdir(d)) != NULL) {
 
131
        if (de->d_name[0] > '/' /* skip ., .., / and '\0'*/
 
132
                && !file_is_lock(de->d_name)) {
 
133
            func(de->d_name, arg);
 
134
        }
 
135
    }
 
136
    closedir(d);
 
137
}
 
138
 
 
139
static void fs_clear_locks(Store *store)
 
140
{
 
141
    struct dirent *de;
 
142
    DIR *d = opendir(store->dir.path);
 
143
 
 
144
    if (!d) {
 
145
        RAISE(IO_ERROR, "clearing locks in %s: <%s>",
 
146
              store->dir.path, strerror(errno));
 
147
    }
 
148
 
 
149
    while ((de = readdir(d)) != NULL) {
 
150
        if (file_is_lock(de->d_name)) {
 
151
            char path[MAX_FILE_PATH];
 
152
            remove(join_path(path, store->dir.path, de->d_name));
 
153
        }
 
154
    }
 
155
    closedir(d);
 
156
}
 
157
 
 
158
static void fs_clear(Store *store)
 
159
{
 
160
    struct dirent *de;
 
161
    DIR *d = opendir(store->dir.path);
 
162
 
 
163
    if (!d) {
 
164
        RAISE(IO_ERROR, "clearing files in %s: <%s>",
 
165
              store->dir.path, strerror(errno));
 
166
    }
 
167
 
 
168
    while ((de = readdir(d)) != NULL) {
 
169
        if (de->d_name[0] > '/' /* skip ., .., / and '\0'*/
 
170
                && !file_is_lock(de->d_name)) {
 
171
            char path[MAX_FILE_PATH];
 
172
            remove(join_path(path, store->dir.path, de->d_name));
 
173
        }
 
174
    }
 
175
    closedir(d);
 
176
}
 
177
 
 
178
static void fs_clear_all(Store *store)
 
179
{
 
180
    struct dirent *de;
 
181
    DIR *d = opendir(store->dir.path);
 
182
 
 
183
    if (!d) {
 
184
        RAISE(IO_ERROR, "clearing all files in %s: <%s>",
 
185
              store->dir.path, strerror(errno));
 
186
    }
 
187
 
 
188
    while ((de = readdir(d)) != NULL) {
 
189
        if (de->d_name[0] > '/') { /* skip ., .., / and '\0'*/
 
190
            char path[MAX_FILE_PATH];
 
191
            remove(join_path(path, store->dir.path, de->d_name));
 
192
        }
 
193
    }
 
194
    closedir(d);
 
195
}
 
196
 
 
197
/**
 
198
 * Destroy the store.
 
199
 *
 
200
 * @param p the store to destroy
 
201
 * @raise IO_ERROR if there is an error deleting the locks
 
202
 */
 
203
static void fs_destroy(Store *store)
 
204
{
 
205
    TRY
 
206
        fs_clear_locks(store);
 
207
    XCATCHALL
 
208
        HANDLED();
 
209
    XENDTRY
 
210
    free(store->dir.path);
 
211
    store_destroy(store);
 
212
}
 
213
 
 
214
static off_t fs_length(Store *store, char *filename)
 
215
{
 
216
    char path[MAX_FILE_PATH];
 
217
    struct stat stt;
 
218
 
 
219
    if (stat(join_path(path, store->dir.path, filename), &stt)) {
 
220
        RAISE(IO_ERROR, "getting lenth of %s: <%s>", path,
 
221
              strerror(errno));
 
222
    }
 
223
 
 
224
    return stt.st_size;
 
225
}
 
226
 
 
227
static void fso_flush_i(OutStream *os, uchar *src, int len)
 
228
{
 
229
    if (len != write(os->file.fd, src, len)) {
 
230
        RAISE(IO_ERROR, "flushing src of length %d, <%s>", len,
 
231
              strerror(errno));
 
232
    }
 
233
}
 
234
 
 
235
static void fso_seek_i(OutStream *os, off_t pos)
 
236
{
 
237
    if (lseek(os->file.fd, pos, SEEK_SET) < 0) {
 
238
        RAISE(IO_ERROR, "seeking position %"F_OFF_T_PFX"d: <%s>",
 
239
              pos, strerror(errno));
 
240
    }
 
241
}
 
242
 
 
243
static void fso_close_i(OutStream *os)
 
244
{
 
245
    if (close(os->file.fd)) {
 
246
        RAISE(IO_ERROR, "closing file: <%s>", strerror(errno));
 
247
    }
 
248
}
 
249
 
 
250
const struct OutStreamMethods FS_OUT_STREAM_METHODS = {
 
251
    fso_flush_i,
 
252
    fso_seek_i,
 
253
    fso_close_i
 
254
};
 
255
 
 
256
static OutStream *fs_new_output(Store *store, const char *filename)
 
257
{
 
258
    char path[MAX_FILE_PATH];
 
259
    int fd = open(join_path(path, store->dir.path, filename),
 
260
                  O_WRONLY | O_CREAT | O_BINARY, store->file_mode);
 
261
    OutStream *os;
 
262
    if (fd < 0) {
 
263
        RAISE(IO_ERROR, "couldn't create OutStream %s: <%s>",
 
264
              path, strerror(errno));
 
265
    }
 
266
 
 
267
    os = os_new();
 
268
    os->file.fd = fd;
 
269
    os->m = &FS_OUT_STREAM_METHODS;
 
270
    return os;
 
271
}
 
272
 
 
273
static void fsi_read_i(InStream *is, uchar *path, int len)
 
274
{
 
275
    int fd = is->file.fd;
 
276
    off_t pos = is_pos(is);
 
277
    if (pos != lseek(fd, 0, SEEK_CUR)) {
 
278
        lseek(fd, pos, SEEK_SET);
 
279
    }
 
280
    if (read(fd, path, len) != len) {
 
281
        /* win: the wrong value can be returned for some reason so double check */
 
282
        if (lseek(fd, 0, SEEK_CUR) != (pos + len)) {
 
283
            RAISE(IO_ERROR, "couldn't read %d chars from %s: <%s>",
 
284
                  len, path, strerror(errno));
 
285
        }
 
286
    }
 
287
}
 
288
 
 
289
static void fsi_seek_i(InStream *is, off_t pos)
 
290
{
 
291
    if (lseek(is->file.fd, pos, SEEK_SET) < 0) {
 
292
        RAISE(IO_ERROR, "seeking pos %"F_OFF_T_PFX"d: <%s>",
 
293
              pos, strerror(errno));
 
294
    }
 
295
}
 
296
 
 
297
static void fsi_close_i(InStream *is)
 
298
{
 
299
    if (close(is->file.fd)) {
 
300
        RAISE(IO_ERROR, strerror(errno));
 
301
    }
 
302
    free(is->d.path);
 
303
}
 
304
 
 
305
static off_t fsi_length_i(InStream *is)
 
306
{
 
307
    struct stat stt;
 
308
    if (fstat(is->file.fd, &stt)) {
 
309
        RAISE(IO_ERROR, "fstat failed: <%s>", strerror(errno));
 
310
    }
 
311
    return stt.st_size;
 
312
}
 
313
 
 
314
static const struct InStreamMethods FS_IN_STREAM_METHODS = {
 
315
    fsi_read_i,
 
316
    fsi_seek_i,
 
317
    fsi_length_i,
 
318
    fsi_close_i
 
319
};
 
320
 
 
321
static InStream *fs_open_input(Store *store, const char *filename)
 
322
{
 
323
    InStream *is;
 
324
    char path[MAX_FILE_PATH];
 
325
    int fd = open(join_path(path, store->dir.path, filename), O_RDONLY | O_BINARY);
 
326
    if (fd < 0) {
 
327
        RAISE(FILE_NOT_FOUND_ERROR,
 
328
              "tried to open \"%s\" but it doesn't exist: <%s>",
 
329
              path, strerror(errno));
 
330
    }
 
331
    is = is_new();
 
332
    is->file.fd = fd;
 
333
    is->d.path = estrdup(path);
 
334
    is->m = &FS_IN_STREAM_METHODS;
 
335
    return is;
 
336
}
 
337
 
 
338
#define LOCK_OBTAIN_TIMEOUT 10
 
339
 
 
340
#ifdef RUBY_BINDINGS
 
341
struct timeval rb_time_interval _((VALUE));
 
342
#endif
 
343
 
 
344
static int fs_lock_obtain(Lock *lock)
 
345
{
 
346
    int f;
 
347
    int trys = LOCK_OBTAIN_TIMEOUT;
 
348
    while (((f =
 
349
             open(lock->name, O_CREAT | O_EXCL | O_RDWR,
 
350
                   S_IRUSR | S_IWUSR)) < 0) && (trys > 0)) {
 
351
 
 
352
        /* sleep for 10 milliseconds */
 
353
        micro_sleep(10000);
 
354
        trys--;
 
355
    }
 
356
    if (f >= 0) {
 
357
        close(f);
 
358
        return true;
 
359
    }
 
360
    else {
 
361
        return false;
 
362
    }
 
363
}
 
364
 
 
365
static int fs_lock_is_locked(Lock *lock)
 
366
{
 
367
    int f = open(lock->name, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
 
368
    if (f >= 0) {
 
369
        if (close(f) || remove(lock->name)) {
 
370
            RAISE(IO_ERROR, "couldn't close lock \"%s\": <%s>", lock->name,
 
371
                  strerror(errno));
 
372
        }
 
373
        return false;
 
374
    }
 
375
    else {
 
376
        return true;
 
377
    }
 
378
}
 
379
 
 
380
void fs_lock_release(Lock *lock)
 
381
{
 
382
    remove(lock->name);
 
383
}
 
384
 
 
385
static Lock *fs_open_lock_i(Store *store, char *lockname)
 
386
{
 
387
    Lock *lock = ALLOC(Lock);
 
388
    char lname[100];
 
389
    char path[MAX_FILE_PATH];
 
390
    snprintf(lname, 100, "%s%s.lck", LOCK_PREFIX, lockname);
 
391
    lock->name = estrdup(join_path(path, store->dir.path, lname));
 
392
    lock->store = store;
 
393
    lock->obtain = &fs_lock_obtain;
 
394
    lock->release = &fs_lock_release;
 
395
    lock->is_locked = &fs_lock_is_locked;
 
396
    return lock;
 
397
}
 
398
 
 
399
static void fs_close_lock_i(Lock *lock)
 
400
{
 
401
    remove(lock->name);
 
402
    free(lock->name);
 
403
    free(lock);
 
404
}
 
405
 
 
406
static HashTable stores = {
 
407
    /* fill */       0,
 
408
    /* used */       0,
 
409
    /* mask */       HASH_MINSIZE - 1,
 
410
    /* ref_cnt */    1,
 
411
    /* table */      stores.smalltable,
 
412
    /* smalltable */ {{0, NULL, NULL}},
 
413
    /* lookup */     (h_lookup_ft)&h_lookup_str,
 
414
    /* hash */       NULL,
 
415
    /* eq */         NULL,
 
416
    /* free_key */   (free_ft)&dummy_free,
 
417
    /* free_value */ (free_ft)&fs_destroy
 
418
};
 
419
 
 
420
#ifndef UNTHREADED
 
421
static mutex_t stores_mutex = MUTEX_INITIALIZER;
 
422
#endif
 
423
 
 
424
static void fs_close_i(Store *store)
 
425
{
 
426
    mutex_lock(&stores_mutex);
 
427
    h_del(&stores, store->dir.path);
 
428
    mutex_unlock(&stores_mutex);
 
429
}
 
430
 
 
431
static Store *fs_store_new(const char *pathname)
 
432
{
 
433
    struct stat stt;
 
434
    Store *new_store = store_new();
 
435
 
 
436
    new_store->file_mode = S_IRUSR | S_IWUSR;
 
437
#ifndef POSH_OS_WIN32
 
438
    if (!stat(pathname, &stt) && stt.st_gid == getgid()) {
 
439
        if (stt.st_mode & S_IWGRP) {
 
440
            umask(S_IWOTH);
 
441
        }
 
442
        new_store->file_mode |= stt.st_mode & (S_IRGRP | S_IWGRP);
 
443
    }
 
444
#endif
 
445
 
 
446
    new_store->dir.path      = estrdup(pathname);
 
447
    new_store->touch         = &fs_touch;
 
448
    new_store->exists        = &fs_exists;
 
449
    new_store->remove        = &fs_remove;
 
450
    new_store->rename        = &fs_rename;
 
451
    new_store->count         = &fs_count;
 
452
    new_store->close_i       = &fs_close_i;
 
453
    new_store->clear         = &fs_clear;
 
454
    new_store->clear_all     = &fs_clear_all;
 
455
    new_store->clear_locks   = &fs_clear_locks;
 
456
    new_store->length        = &fs_length;
 
457
    new_store->each          = &fs_each;
 
458
    new_store->new_output    = &fs_new_output;
 
459
    new_store->open_input    = &fs_open_input;
 
460
    new_store->open_lock_i   = &fs_open_lock_i;
 
461
    new_store->close_lock_i  = &fs_close_lock_i;
 
462
    return new_store;
 
463
}
 
464
 
 
465
Store *open_fs_store(const char *pathname)
 
466
{
 
467
    Store *store = NULL;
 
468
 
 
469
    mutex_lock(&stores_mutex);
 
470
    store = h_get(&stores, pathname);
 
471
    if (store) {
 
472
        mutex_lock(&store->mutex);
 
473
        store->ref_cnt++;
 
474
        mutex_unlock(&store->mutex);
 
475
    }
 
476
    else {
 
477
        store = fs_store_new(pathname);
 
478
        h_set(&stores, store->dir.path, store);
 
479
    }
 
480
    mutex_unlock(&stores_mutex);
 
481
 
 
482
    return store;
 
483
}