Annotation of uae/src/filesys.c, revision 1.1.1.1

1.1       root        1:  /*
                      2:   * UAE - The Un*x Amiga Emulator
                      3:   *
                      4:   * Unix file system handler for AmigaDOS
                      5:   *
                      6:   * Copyright 1996 Ed Hanway, Bernd Schmidt
                      7:   *
                      8:   * Version 0.2: 960730
                      9:   *
                     10:   * Based on example code (c) 1988 The Software Distillery
                     11:   * and published in Transactor for the Amiga, Volume 2, Issues 2-5.
                     12:   * (May - August 1989)
                     13:   *
                     14:   * Known limitations:
                     15:   * Does not support ACTION_INHIBIT (big deal).
                     16:   * Does not support any 2.0+ packet types (except ACTION_SAME_LOCK)
                     17:   * Does not actually enforce exclusive locks.
                     18:   * Does not support removable volumes.
                     19:   * May not return the correct error code in some cases.
                     20:   * Does not check for sane values passed by AmigaDOS.  May crash the emulation
                     21:   * if passed garbage values.
                     22:   *
                     23:   * TODO someday:
                     24:   * Implement real locking using flock.  Needs test cases.
                     25:   */
                     26: 
                     27: #include "sysconfig.h"
                     28: #include "sysdeps.h"
                     29: 
                     30: #include "config.h"
                     31: #include "options.h"
                     32: #include "memory.h"
                     33: #include "custom.h"
                     34: #include "events.h"
                     35: #include "newcpu.h"
                     36: #include "xwin.h"
                     37: #include "autoconf.h"
                     38: #include "compiler.h"
                     39: 
                     40: #ifdef __bebox__
                     41: #undef access
                     42: // This is just a temporary hack.
                     43: // (Will be obsolete in future versions of the BeOS) [ So I hope -- Bernd ]
                     44: 
                     45: int access(const char *name, int mode)
                     46: {
                     47:     struct stat statbuf;
                     48: //     printf("%s\n",name);
                     49: 
                     50:     if(-1 == stat(name, &statbuf)) return(-1);
                     51:     else{
                     52: //             printf("OK\n");
                     53: //             printf("%i\n",S_ISDIR(statbuf.st_mode)?1:0);
                     54:                if(mode&R_OK) if(statbuf.st_mode&S_IRUSR) return(0);
                     55:                if(mode&W_OK) if(statbuf.st_mode&S_IWUSR) return(0);
                     56:                return(-1);
                     57:        }
                     58: }
                     59: #endif
                     60: 
                     61: #define MAKE_CASE_INSENSITIVE
                     62: 
                     63: typedef struct {
                     64:     char *devname; /* device name, e.g. UAE0: */
                     65:     CPTR devname_amiga;
                     66:     CPTR startup;
                     67:     char *volname; /* volume name, e.g. CDROM, WORK, etc. */
                     68:     char *rootdir; /* root unix directory */
                     69:     int readonly; /* disallow write access? */
                     70:     int devno;
                     71: } UnitInfo;
                     72: 
                     73: #define MAX_UNITS 20
                     74: static int num_units = 0, num_filesys_units = 0;
                     75: static UnitInfo ui[MAX_UNITS];
                     76: 
                     77: static ULONG fsdevname, filesysseglist, hardfileseglist, filesys_configdev;
                     78: static CPTR filesys_parampacket;
                     79: 
                     80: void add_filesys_unit(char *volname, char *rootdir, int readonly)
                     81: {
                     82:     if (num_units >= MAX_UNITS) {
                     83:        fprintf(stderr, "Maximum number of file systems mounted.\n");
                     84:        return;
                     85:     }
                     86: 
                     87:     if (volname != NULL) {
                     88:        num_filesys_units++;
                     89:        ui[num_units].volname = my_strdup(volname);
                     90:     } else
                     91:        ui[num_units].volname = NULL;
                     92:     ui[num_units].rootdir = my_strdup(rootdir);
                     93:     ui[num_units].readonly = readonly;
                     94: 
                     95:     num_units++;
                     96: }
                     97: 
                     98: void write_filesys_config(FILE *f)
                     99: {
                    100:     int i;
                    101:     for (i = 0; i < num_units; i++) {
                    102:        if (ui[i].volname != NULL) {
                    103:            fprintf(f, "-%c %s:%s\n", ui[i].readonly ? 'M' : 'm',
                    104:                    ui[i].volname, ui[i].rootdir);
                    105:        }
                    106:     }
                    107: }
                    108: 
                    109: #ifdef TRACING_ENABLED
                    110: #define TRACE(x)       printf x;
                    111: #define DUMPLOCK(x)    dumplock(x)
                    112: #else
                    113: #define TRACE(x)
                    114: #define DUMPLOCK(x)
                    115: #endif
                    116: 
                    117: /* minimal AmigaDOS definitions */
                    118: 
                    119: /* field offsets in DosPacket */
                    120: #define dp_Type (8)
                    121: #define dp_Res1        (12)
                    122: #define dp_Res2 (16)
                    123: #define dp_Arg1 (20)
                    124: #define dp_Arg2 (24)
                    125: #define dp_Arg3 (28)
                    126: #define dp_Arg4 (32)
                    127: 
                    128: /* result codes */
                    129: #define DOS_TRUE (-1L)
                    130: #define DOS_FALSE (0L)
                    131: 
                    132: /* packet types */
                    133: #define ACTION_CURRENT_VOLUME  7
                    134: #define ACTION_LOCATE_OBJECT   8
                    135: #define ACTION_RENAME_DISK     9
                    136: #define ACTION_FREE_LOCK       15
                    137: #define ACTION_DELETE_OBJECT   16
                    138: #define ACTION_RENAME_OBJECT   17
                    139: #define ACTION_COPY_DIR                19
                    140: #define ACTION_SET_PROTECT     21
                    141: #define ACTION_CREATE_DIR      22
                    142: #define ACTION_EXAMINE_OBJECT  23
                    143: #define ACTION_EXAMINE_NEXT    24
                    144: #define ACTION_DISK_INFO       25
                    145: #define ACTION_INFO            26
                    146: #define ACTION_FLUSH           27
                    147: #define ACTION_SET_COMMENT     28
                    148: #define ACTION_PARENT          29
                    149: #define ACTION_SET_DATE                34
                    150: #define ACTION_SAME_LOCK       40
                    151: #define ACTION_FIND_WRITE      1004
                    152: #define ACTION_FIND_INPUT      1005
                    153: #define ACTION_FIND_OUTPUT     1006
                    154: #define ACTION_END             1007
                    155: #define ACTION_SEEK            1008
                    156: #define ACTION_IS_FILESYSTEM   1027
                    157: #define ACTION_READ            'R'
                    158: #define ACTION_WRITE           'W'
                    159: 
                    160: #define DISK_TYPE              (0x444f5301) /* DOS\1 */
                    161: 
                    162: /* errors */
                    163: #define ERROR_NO_FREE_STORE     103
                    164: #define ERROR_OBJECT_IN_USE    202
                    165: #define ERROR_OBJECT_EXISTS     203
                    166: #define ERROR_DIR_NOT_FOUND     204
                    167: #define ERROR_OBJECT_NOT_FOUND  205
                    168: #define ERROR_ACTION_NOT_KNOWN  209
                    169: #define ERROR_OBJECT_WRONG_TYPE 212
                    170: #define ERROR_DISK_WRITE_PROTECTED 214
                    171: #define ERROR_DIRECTORY_NOT_EMPTY 216
                    172: #define ERROR_DEVICE_NOT_MOUNTED 218
                    173: #define ERROR_SEEK_ERROR       219
                    174: #define ERROR_DISK_FULL                221
                    175: #define ERROR_WRITE_PROTECTED 223
                    176: #define ERROR_NO_MORE_ENTRIES  232
                    177: #define ERROR_NOT_IMPLEMENTED  236
                    178: 
                    179: static long dos_errno(void)
                    180: {
                    181:     int e = errno;
                    182: 
                    183:     switch(e) {
                    184:      case ENOMEM:      return ERROR_NO_FREE_STORE;
                    185:      case EEXIST:      return ERROR_OBJECT_EXISTS;
                    186:      case EACCES:      return ERROR_WRITE_PROTECTED;
                    187:      case ENOENT:      return ERROR_OBJECT_NOT_FOUND;
                    188:      case ENOTDIR:     return ERROR_OBJECT_WRONG_TYPE;
                    189:      case ENOSPC:      return ERROR_DISK_FULL;
                    190:      case EBUSY:               return ERROR_OBJECT_IN_USE;
                    191:      case EISDIR:      return ERROR_OBJECT_WRONG_TYPE;
                    192: #if defined(ETXTBSY)
                    193:      case ETXTBSY:     return ERROR_OBJECT_IN_USE;
                    194: #endif
                    195: #if defined(EROFS)
                    196:      case EROFS:               return ERROR_DISK_WRITE_PROTECTED;
                    197: #endif
                    198: #if defined(ENOTEMPTY)
                    199: #if ENOTEMPTY != EEXIST
                    200:      case ENOTEMPTY:   return ERROR_DIRECTORY_NOT_EMPTY;
                    201: #endif
                    202: #endif
                    203: 
                    204:      default:
                    205:        TRACE(("Unimplemented error %s\n", strerror(e)));
                    206:        return ERROR_NOT_IMPLEMENTED;
                    207:     }
                    208: }
                    209: 
                    210: /* handler state info */
                    211: 
                    212: typedef struct _unit {
                    213:     struct _unit *next;
                    214: 
                    215:     /* Amiga stuff */
                    216:     CPTR       dosbase;
                    217:     CPTR       volume;
                    218:     CPTR       port;   /* Our port */
                    219: 
                    220:     /* Native stuff */
                    221:     long       unit;   /* unit number */
                    222:     UnitInfo   ui;     /* unit startup info */
                    223: } Unit;
                    224: 
                    225: typedef struct {
                    226:     CPTR addr; /* addr of real packet */
                    227:     long type;
                    228:     long res1;
                    229:     long res2;
                    230:     long arg1;
                    231:     long arg2;
                    232:     long arg3;
                    233:     long arg4;
                    234: } DosPacket;
                    235: 
                    236: static char *
                    237: bstr(CPTR addr)
                    238: {
                    239:     static char buf[256];
                    240:     int n = get_byte(addr++);
                    241:     int i;
                    242:     for(i = 0; i < n; i++)
                    243:        buf[i] = get_byte(addr++);
                    244:     buf[i] = 0;
                    245:     return buf;
                    246: }
                    247: 
                    248: static Unit *units = NULL;
                    249: static int unit_num = 0;
                    250: 
                    251: static Unit*
                    252: find_unit(CPTR port)
                    253: {
                    254:     Unit* u;
                    255:     for(u = units; u; u = u->next)
                    256:        if(u->port == port)
                    257:            break;
                    258: 
                    259:     return u;
                    260: }
                    261: 
                    262: static CPTR DosAllocMem(ULONG len)
                    263: {
                    264:     ULONG i;
                    265:     CPTR addr;
                    266: 
                    267:     regs.d[0] = len + 4;
                    268:     regs.d[1] = 1; /* MEMF_PUBLIC */
                    269:     addr = CallLib(regs.a[6], -198); /* AllocMem */
                    270: 
                    271:     if(addr) {
                    272:        put_long(addr, len);
                    273:        addr += 4;
                    274: 
                    275:        /* faster to clear memory here rather than use MEMF_CLEAR */
                    276:        for(i = 0; i < len; i += 4)
                    277:            put_long(addr + i, 0);
                    278:     }
                    279: 
                    280:     return addr;
                    281: }
                    282: 
                    283: static void DosFreeMem(CPTR addr)
                    284: {
                    285:     addr -= 4;
                    286:     regs.d[0] = get_long(addr) + 4;
                    287:     regs.a[1] = addr;
                    288:     CallLib(regs.a[6], -210); /* FreeMem */
                    289: }
                    290: 
                    291: static void
                    292: startup(DosPacket* packet)
                    293: {
                    294:     int i, namelen;
                    295:     char* devname = bstr(packet->arg1 << 2);
                    296:     char* s;
                    297:     Unit* unit;
                    298: 
                    299:     /* find UnitInfo with correct device name */
                    300:     s = strchr(devname, ':');
                    301:     if(s) *s = '\0';
                    302: 
                    303:     for(i = 0; i < num_units; i++) {
                    304:        /* Hardfile volume name? */
                    305:        if (ui[i].volname == NULL)
                    306:            continue;
                    307:            
                    308:        if (ui[i].startup == packet->arg2)
                    309:            break;          
                    310:     }
                    311:     
                    312:     if(i == num_units || 0 != access(ui[i].rootdir, R_OK)) {
                    313:        fprintf(stderr, "Failed attempt to mount device\n", devname);
                    314:        packet->res1 = DOS_FALSE;
                    315:        packet->res2 = ERROR_DEVICE_NOT_MOUNTED;
                    316:        return;
                    317:     }
                    318: 
                    319:     unit = (Unit *) malloc(sizeof(Unit));
                    320:     unit->next = units;
                    321:     units = unit;
                    322: 
                    323:     unit->volume = 0;
                    324:     unit->port = regs.a[5];
                    325:     unit->unit = unit_num++;
                    326: 
                    327:     unit->ui.devname = ui[i].devname;
                    328:     unit->ui.volname = my_strdup(ui[i].volname); /* might free later for rename */
                    329:     unit->ui.rootdir = ui[i].rootdir;
                    330:     unit->ui.readonly = ui[i].readonly;
                    331: 
                    332:     TRACE(("**** STARTUP volume %s\n", unit->ui.volname));
                    333: 
                    334:     /* fill in our process in the device node */
                    335:     put_long((packet->arg3 << 2) + 8, unit->port);
                    336: 
                    337:     /* open dos.library */
                    338:     regs.d[0] = 0;
                    339:     regs.a[1] = EXPANSION_doslibname;
                    340:     unit->dosbase = CallLib(regs.a[6], -552); /* OpenLibrary */
                    341: 
                    342:     {
                    343:        CPTR rootnode = get_long(unit->dosbase + 34);
                    344:        CPTR dos_info = get_long(rootnode + 24) << 2;
                    345:        /* make new volume */
                    346:        unit->volume = DosAllocMem(80 + 1 + 44);
                    347:        put_long(unit->volume + 4, 2); /* Type = dt_volume */
                    348:        put_long(unit->volume + 12, 0); /* Lock */
                    349:        put_long(unit->volume + 16, 3800); /* Creation Date */
                    350:        put_long(unit->volume + 20, 0);
                    351:        put_long(unit->volume + 24, 0);
                    352:        put_long(unit->volume + 28, 0); /* lock list */
                    353:        put_long(unit->volume + 40, (unit->volume + 44) >> 2); /* Name */
                    354:        namelen = strlen(unit->ui.volname);
                    355:        put_byte(unit->volume + 44, namelen);
                    356:        for(i = 0; i < namelen; i++)
                    357:            put_byte(unit->volume + 45 + i, unit->ui.volname[i]);
                    358:        
                    359:        /* link into DOS list */
                    360:        put_long(unit->volume, get_long(dos_info + 4));
                    361:        put_long(dos_info + 4, unit->volume >> 2);
                    362:     }
                    363:     
                    364:     put_long(unit->volume + 8, unit->port);
                    365:     put_long(unit->volume + 32, DISK_TYPE);
                    366: 
                    367:     packet->res1 = DOS_TRUE;
                    368: }
                    369: 
                    370: #ifdef HAVE_STATFS
                    371: static void
                    372: do_info(Unit* unit, DosPacket* packet, CPTR info)
                    373: {
                    374:     struct statfs statbuf;
                    375: #if STATFS_NO_ARGS == 2
                    376:     if(-1 == statfs(unit->ui.rootdir, &statbuf))
                    377: #else
                    378:     if(-1 == statfs(unit->ui.rootdir, &statbuf, sizeof(struct statfs), 0))
                    379: #endif
                    380:     {
                    381:        packet->res1 = DOS_FALSE;
                    382:        packet->res2 = dos_errno();
                    383:     }
                    384: 
                    385:     put_long(info, 0); /* errors */
                    386:     put_long(info + 4, unit->unit); /* unit number */
                    387:     put_long(info + 8, unit->ui.readonly ? 80 : 82); /* state  */
                    388:     put_long(info + 12, statbuf.f_blocks); /* numblocks */
                    389:     put_long(info + 16, statbuf.f_blocks - statbuf.STATBUF_BAVAIL); /* inuse */
                    390:     put_long(info + 20, statbuf.f_bsize); /* bytesperblock */
                    391:     put_long(info + 24, DISK_TYPE); /* disk type */
                    392:     put_long(info + 28, unit->volume >> 2); /* volume node */
                    393:     put_long(info + 32, 0); /* inuse */
                    394:     packet->res1 = DOS_TRUE;
                    395: }
                    396: #else
                    397: static void
                    398: do_info(Unit* unit, DosPacket* packet, CPTR info)
                    399: {
                    400:     put_long(info, 0); /* errors */
                    401:     put_long(info + 4, unit->unit); /* unit number */
                    402:     put_long(info + 8, unit->ui.readonly ? 80 : 82); /* state  */
                    403:     put_long(info + 12, 256); /* numblocks */
                    404:     put_long(info + 16, 128); /* inuse */
                    405:     put_long(info + 20, 512); /* bytesperblock */
                    406:     put_long(info + 24, DISK_TYPE); /* disk type */
                    407:     put_long(info + 28, unit->volume >> 2); /* volume node */
                    408:     put_long(info + 32, 0); /* inuse */
                    409:     packet->res1 = DOS_TRUE;
                    410: }
                    411: #endif
                    412: 
                    413: static void
                    414: action_disk_info(Unit* unit, DosPacket* packet)
                    415: {
                    416:     TRACE(("ACTION_DISK_INFO\n"));
                    417:     do_info(unit, packet, packet->arg1 << 2);
                    418: }
                    419: 
                    420: static void
                    421: action_info(Unit* unit, DosPacket* packet)
                    422: {
                    423:     TRACE(("ACTION_INFO\n"));
                    424:     do_info(unit, packet, packet->arg2 << 2);
                    425: }
                    426: 
                    427: typedef struct key {
                    428:     struct key *next;
                    429:     ULONG uniq;
                    430:     char *path;
                    431:     int fd;
                    432:     off_t file_pos;
                    433: } Key;
                    434: 
                    435: static struct key* keys = NULL;
                    436: 
                    437: static void
                    438: free_key(Key*k)
                    439: {
                    440:     Key *k1;
                    441:     Key *prev = NULL;
                    442:     for(k1 = keys; k1; k1 = k1->next) {
                    443:         if(k == k1) {
                    444:             if(prev)
                    445:                 prev->next = k->next;
                    446:             else
                    447:                 keys = k->next;
                    448:             break;
                    449:         }
                    450:         prev = k1;
                    451:     }
                    452: 
                    453:     if (k->fd >= 0)
                    454:        close(k->fd);
                    455:     free(k->path);
                    456:     free(k);
                    457: }
                    458: 
                    459: static Key*
                    460: lookup_key(ULONG uniq)
                    461: {
                    462:     Key *k;
                    463:     for(k = keys; k; k = k->next) {
                    464:         if(uniq == k->uniq)
                    465:             return k;
                    466:     }
                    467:     fprintf(stderr, "Error: couldn't find key %ld\n", uniq);
                    468:     exit(1);
                    469:     /* NOTREACHED */
                    470:     return NULL;
                    471: }
                    472: 
                    473: static Key*
                    474: new_key(void)
                    475: {
                    476:     static ULONG uniq = 0;
                    477:     Key *k = (Key*) malloc(sizeof(Key));
                    478:     k->uniq = ++uniq;
                    479:     k->fd = -1;
                    480:     k->file_pos = 0;
                    481:     k->next = keys;
                    482:     keys = k;
                    483: 
                    484:     return k;
                    485: }
                    486: 
                    487: static void
                    488: dumplock(CPTR lock)
                    489: {
                    490:     if(!lock) {
                    491:        fprintf(stderr, "LOCK: 0x0\n");
                    492:        return;
                    493:     }
                    494:     fprintf(stderr,
                    495:            "LOCK: 0x%lx { next=0x%lx, key=%s, mode=%ld, handler=0x%lx, volume=0x%lx }\n",
                    496:            lock,
                    497:            get_long(lock)<<2, lookup_key(get_long(lock+4))->path, get_long(lock+8),
                    498:            get_long(lock+12), get_long(lock+16));
                    499: }
                    500: 
                    501: static char*
                    502: get_path(Unit* unit, const char *base, const char *rel)
                    503: {
                    504:     static char buf[1024];
                    505:     char *s = buf;
                    506:     char *p;
                    507:     char *r;
                    508: 
                    509:     int i;
                    510: 
                    511:     TRACE(("get_path(%s,%s)\n", base, rel));
                    512: 
                    513:     /* root-relative path? */
                    514:     for(i = 0; rel[i] && rel[i] != '/' && rel[i] != ':'; i++);
                    515:     if(':' == rel[i]) {
                    516:        /*base = unit->ui.rootdir;*/ rel += i+1;
                    517:     }
                    518: 
                    519:     while(*base) {
                    520:        *s++ = *base++;
                    521:     }
                    522:     *s = 0;
                    523:     p = s; /* start of relative path */
                    524:     r = buf + strlen(unit->ui.rootdir); /* end of fixed path */
                    525: 
                    526:     while(*rel) {
                    527:        /* start with a slash? go up a level. */
                    528:        if('/' == *rel) {
                    529:            while(s > r && '/' != *s)
                    530:                s--;
                    531:            *s = 0;
                    532:            rel++;
                    533:        } else {
                    534:            *s++ = '/';
                    535:            while(*rel && '/' != *rel) {
                    536:                *s++ = *rel++;
                    537:            }
                    538:            *s = 0;
                    539:            if('/' == *rel)
                    540:                rel++;
                    541:        }
                    542:     }
                    543:     *s = 0;
                    544: 
                    545: #ifdef MAKE_CASE_INSENSITIVE
                    546:     TRACE(("path=\"%s\"\n", buf));
                    547:     /* go through each section of the path and if it does not exist,
                    548:      * scan its directory for any case insensitive matches
                    549:      */
                    550:     while(*p) {
                    551:        char *p2 = strchr(p+1, '/');
                    552:        char oldp2;
                    553:        if(!p2) {
                    554:            p2 = p+1;
                    555:            while(*p2) p2++;
                    556:        }
                    557:        oldp2 = *p2;
                    558:        *p2 = '\0';
                    559:        if(0 != access(buf, F_OK|R_OK)) {
                    560:            DIR* dir;
                    561:            struct dirent* de;
                    562:            /* does not exist -- check dir for case insensitive match */
                    563:            *p++ = '\0'; /* was '/' */
                    564:            dir = opendir(buf);
                    565:            if (dir) {
                    566:                while((de = readdir(dir))) {
                    567: #if 0
                    568:                    if(0 == stricmp(de->d_name, p))     /* OLSEN */
                    569: #endif
                    570:                    if(0 == strcasecmp(de->d_name, p))
                    571:                        break;
                    572:                }
                    573:                if(de) {
                    574:                    strcpy(p, de->d_name);
                    575:                }
                    576:                closedir(dir);
                    577:            }
                    578:            *--p = '/';
                    579:        }
                    580:        *p2 = oldp2;
                    581:        p = p2;
                    582:     }
                    583: #endif
                    584:     TRACE(("path=\"%s\"\n", buf));
                    585: 
                    586:     return my_strdup(buf);
                    587: }
                    588: 
                    589: static Key*
                    590: make_key(Unit* unit, CPTR lock, const char *name)
                    591: {
                    592:     Key *k = new_key();
                    593: 
                    594:     if(!lock) {
                    595:        k->path = get_path(unit, unit->ui.rootdir, name);
                    596:     } else {
                    597:        Key*oldk = lookup_key(get_long(lock + 4));
                    598: #if 0
                    599:        const char *nm = strchr (name, ':');
                    600:        if (nm == NULL) {
                    601:            nm = name;
                    602:        } else
                    603:            nm++;
                    604: #else
                    605:        const char *nm = name;
                    606: #endif
                    607:        TRACE(("key: 0x%08lx", oldk->uniq));
                    608:        TRACE((" \"%s\"\n", oldk->path));
                    609:        k->path = get_path(unit, oldk->path, nm);
                    610:     }
                    611: 
                    612:     TRACE(("key=\"%s\"\n", k->path));
                    613:     return k;
                    614: }
                    615: 
                    616: static Key*
                    617: dup_key(Key*k)
                    618: {
                    619:     Key *newk = new_key();
                    620:     newk->path = my_strdup(k->path);
                    621:     return newk;
                    622: }
                    623: 
                    624: static CPTR
                    625: make_lock(Unit* unit, Key *key, long mode)
                    626: {
                    627:     /* allocate lock */
                    628:     CPTR lock = DosAllocMem(20);
                    629: 
                    630:     put_long(lock + 4, key->uniq);
                    631:     put_long(lock + 8, mode);
                    632:     put_long(lock + 12, unit->port);
                    633:     put_long(lock + 16, unit->volume >> 2);
                    634: 
                    635:     /* prepend to lock chain */
                    636:     put_long(lock, get_long(unit->volume + 28));
                    637:     put_long(unit->volume + 28, lock >> 2);
                    638: 
                    639:     DUMPLOCK(lock);
                    640:     return lock;
                    641: }
                    642: 
                    643: static void
                    644: free_lock(Unit* unit, CPTR lock)
                    645: {
                    646:     if(!lock)
                    647:        return;
                    648: 
                    649:     if(lock == get_long(unit->volume + 28) << 2) {
                    650:        put_long(unit->volume + 28, get_long(lock));
                    651:     } else {
                    652:        CPTR current = get_long(unit->volume + 28);
                    653:        CPTR next = 0;
                    654:        while(current) {
                    655:            next = get_long(current << 2);
                    656:            if(lock == next << 2)
                    657:                break;
                    658:            current = next;
                    659:        }
                    660:        put_long(current << 2, get_long(lock));
                    661:     }
                    662:     free_key(lookup_key(get_long(lock + 4)));
                    663:     DosFreeMem(lock);
                    664: }
                    665: 
                    666: static void
                    667: action_lock(Unit* unit, DosPacket* packet)
                    668: {
                    669:     CPTR lock = packet->arg1 << 2;
                    670:     CPTR name = packet->arg2 << 2;
                    671:     long mode = packet->arg3;
                    672:     int access_mode = (mode == -2) ? R_OK : R_OK|W_OK;
                    673:     Key *k;
                    674: 
                    675:     TRACE(("ACTION_LOCK(0x%lx, \"%s\", %d)\n",lock, bstr(name), mode));
                    676:     DUMPLOCK(lock);
                    677: 
                    678:     k = make_key(unit, lock, bstr(name));
                    679: 
                    680:     if(k && 0 == access(k->path, access_mode)) {
                    681:        packet->res1 = make_lock(unit, k, mode) >> 2;
                    682:     } else {
                    683:        if(k)
                    684:            free_key(k);
                    685:        packet->res1 = 0;
                    686:        packet->res2 = ERROR_OBJECT_NOT_FOUND;
                    687:     }
                    688: }
                    689: 
                    690: static void
                    691: action_free_lock(Unit* unit, DosPacket* packet)
                    692: {
                    693:     CPTR lock = packet->arg1 << 2;
                    694:     TRACE(("ACTION_FREE_LOCK(0x%lx)\n", lock));
                    695:     DUMPLOCK(lock);
                    696: 
                    697:     free_lock(unit, lock);
                    698: 
                    699:     packet->res1 = DOS_TRUE;
                    700: }
                    701: 
                    702: static void
                    703: action_dup_lock(Unit* unit, DosPacket* packet)
                    704: {
                    705:     CPTR lock = packet->arg1 << 2;
                    706: 
                    707:     TRACE(("ACTION_DUP_LOCK(0x%lx)\n", lock));
                    708:     DUMPLOCK(lock);
                    709: 
                    710:     if(!lock) {
                    711:        packet->res1 = 0;
                    712:        return;
                    713:     }
                    714: 
                    715:     {
                    716:        CPTR oldkey = get_long(lock + 4);
                    717:        CPTR oldmode = get_long(lock + 8);
                    718:        packet->res1 = make_lock(unit, dup_key(lookup_key(oldkey)), oldmode) >> 2;
                    719:     }
                    720: }
                    721: 
                    722: /* convert time_t to/from AmigaDOS time */
                    723: const int secs_per_day = 24 * 60 * 60;
                    724: const int diff = (8 * 365 + 2) * (24 * 60 * 60);
                    725: 
                    726: static void
                    727: get_time(time_t t, long* days, long* mins, long* ticks)
                    728: {
                    729:     /* time_t is secs since 1-1-1970 */
                    730:     /* days since 1-1-1978 */
                    731:     /* mins since midnight */
                    732:     /* ticks past minute @ 50Hz */
                    733: 
                    734:     t -= diff;
                    735:     *days = t / secs_per_day;
                    736:     t -= *days * secs_per_day;
                    737:     *mins = t / 60;
                    738:     t -= *mins * 60;
                    739:     *ticks = t * 50;
                    740: }
                    741: 
                    742: static time_t
                    743: put_time(long days, long mins, long ticks)
                    744: {
                    745:     time_t t;
                    746:     t = ticks / 50;
                    747:     t += mins * 60;
                    748:     t += days * secs_per_day;
                    749:     t += diff;
                    750: 
                    751:     return t;
                    752: }
                    753: 
                    754: 
                    755: typedef struct {
                    756:     ULONG uniq;
                    757:     char *path;
                    758:     DIR* dir;
                    759: } ExamineKey;
                    760: 
                    761: /* Since ACTION_EXAMINE_NEXT is so braindamaged, we have to keep
                    762:  * some of these around
                    763:  */
                    764: 
                    765: #define EXKEYS 100
                    766: static ExamineKey examine_keys[EXKEYS];
                    767: static int next_exkey = 0;
                    768: 
                    769: static void
                    770: free_exkey(ExamineKey* ek)
                    771: {
                    772:     free(ek->path);
                    773:     ek->path = 0;
                    774:     if(ek->dir)
                    775:        closedir(ek->dir);
                    776: }
                    777: 
                    778: static ExamineKey*
                    779: new_exkey(char *path)
                    780: {
                    781:     ULONG uniq = next_exkey;
                    782:     ExamineKey* ek= &examine_keys[next_exkey++];
                    783:     if(next_exkey==EXKEYS)
                    784:        next_exkey = 0;
                    785:     if(ek->path) {
                    786:        free_exkey(ek);
                    787:     }
                    788:     ek->path = my_strdup(path);
                    789:     ek->dir = 0;
                    790:     ek->uniq = uniq;
                    791:     return ek;
                    792: }
                    793: 
                    794: static void
                    795: get_fileinfo(Unit *unit, DosPacket* packet, CPTR info, char *buf)
                    796: {
                    797:     struct stat statbuf;
                    798:     long days, mins, ticks;
                    799: 
                    800:     if(-1 == stat(buf, &statbuf)) {
                    801:        packet->res1 = DOS_FALSE;
                    802:        packet->res2 = dos_errno();
                    803:     } else {
                    804:        put_long(info + 4, S_ISDIR(statbuf.st_mode) ? 2 : -3);
                    805:        {
                    806:            /* file name */
                    807:            int i = 8;
                    808:            int n;
                    809:            char *x;
                    810:            if (strcmp (buf, unit->ui.rootdir) == 0) {
                    811:                x = unit->ui.volname;
                    812:            } else {
                    813:                x = strrchr(buf,'/');
                    814:                if(x)
                    815:                    x++;
                    816:                else
                    817:                    x = buf;
                    818:            }
                    819:            TRACE(("name=\"%s\"\n", x));
                    820:            n = strlen(x);
                    821:            if(n > 106) n = 106;
                    822:            put_byte(info + i++, n);
                    823:            while(n--)
                    824:                put_byte(info + i++, *x++);
                    825:            while(i < 108)
                    826:                put_byte(info + i++, 0);
                    827:        }
                    828:        put_long(info + 116,
                    829:                 (S_IRUSR & statbuf.st_mode ? 0 : (1<<3)) |
                    830:                 (S_IWUSR & statbuf.st_mode ? 0 : (1<<2)) |
                    831: #ifndef __DOS__
                    832:                 (S_IXUSR & statbuf.st_mode ? 0 : (1<<1)) |
                    833: #endif
                    834:                 (S_IWUSR & statbuf.st_mode ? 0 : (1<<0)));
                    835:        put_long(info + 120, S_ISDIR(statbuf.st_mode) ? 2 : -3);
                    836:        put_long(info + 124, statbuf.st_size);
                    837: #ifdef HAVE_ST_BLOCKS
                    838:        put_long(info + 128, statbuf.st_blocks);
                    839: #else
                    840:        put_long(info + 128, statbuf.st_size / 512 + 1);
                    841: #endif
                    842:        get_time(statbuf.st_mtime, &days, &mins, &ticks);
                    843:        put_long(info + 132, days);
                    844:        put_long(info + 136, mins);
                    845:        put_long(info + 140, ticks);
                    846:        put_long(info + 144, 0); /* no comment */
                    847:        packet->res1 = DOS_TRUE;
                    848:     }
                    849: }
                    850: 
                    851: static void
                    852: do_examine(Unit *unit, DosPacket* packet, ExamineKey* ek, CPTR info)
                    853: {
                    854:     static char buf[1024];
                    855:     struct dirent* de;
                    856: 
                    857:     if(!ek->dir) {
                    858:        ek->dir = opendir(ek->path);
                    859:     }
                    860:     if(!ek->dir) {
                    861:        free_exkey(ek);
                    862:        packet->res1 = DOS_FALSE;
                    863:        packet->res2 = ERROR_NO_MORE_ENTRIES;
                    864:        return;
                    865:     }
                    866: 
                    867:     de = readdir(ek->dir);
                    868: 
                    869:     while(de && (0 == strcmp(".", de->d_name) 
                    870:                 || 0 == strcmp("..", de->d_name)))
                    871:     {
                    872:        de = readdir(ek->dir);
                    873:     }
                    874: 
                    875:     if(!de) {
                    876:        TRACE(("no more entries\n"));
                    877:        free_exkey(ek);
                    878:        packet->res1 = DOS_FALSE;
                    879:        packet->res2 = ERROR_NO_MORE_ENTRIES;
                    880:        return;
                    881:     }
                    882: 
                    883:     TRACE(("entry=\"%s\"\n", de->d_name));
                    884: 
                    885:     sprintf(buf, "%s/%s", ek->path, de->d_name);
                    886: 
                    887:     get_fileinfo(unit, packet, info, buf);
                    888: }
                    889: 
                    890: static void
                    891: action_examine_object(Unit* unit, DosPacket* packet)
                    892: {
                    893:     CPTR lock = packet->arg1 << 2;
                    894:     CPTR info = packet->arg2 << 2;
                    895:     char *path;
                    896:     ExamineKey* ek;
                    897: 
                    898:     TRACE(("ACTION_EXAMINE_OBJECT(0x%lx,0x%lx)\n", lock, info));
                    899:     DUMPLOCK(lock);
                    900: 
                    901:     if(!lock) {
                    902:        path = unit->ui.rootdir;
                    903:     } else {
                    904:        Key*k = lookup_key(get_long(lock + 4));
                    905:        path = k->path;
                    906:     }
                    907: 
                    908:     get_fileinfo(unit, packet, info, path);
                    909:     ek = new_exkey(path);
                    910:     put_long(info, ek->uniq);
                    911: }
                    912: 
                    913: static void
                    914: action_examine_next(Unit* unit, DosPacket* packet)
                    915: {
                    916:     CPTR lock = packet->arg1 << 2;
                    917:     CPTR info = packet->arg2 << 2;
                    918: 
                    919:     TRACE(("ACTION_EXAMINE_NEXT(0x%lx,0x%lx)\n", lock, info));
                    920:     DUMPLOCK(lock);
                    921: 
                    922:     do_examine(unit, packet, &examine_keys[get_long(info)], info);
                    923: }
                    924: 
                    925: static void
                    926: do_find(Unit* unit, DosPacket* packet, mode_t mode)
                    927: {
                    928:     CPTR fh = packet->arg1 << 2;
                    929:     CPTR lock = packet->arg2 << 2;
                    930:     CPTR name = packet->arg3 << 2;
                    931:     Key *k;
                    932:     struct stat st;
                    933: 
                    934:     TRACE(("ACTION_FIND_*(0x%lx,0x%lx,\"%s\",%d)\n",fh,lock,bstr(name),mode));
                    935:     DUMPLOCK(lock);
                    936: 
                    937:     k = make_key(unit, lock, bstr(name));
                    938:     if(!k) {
                    939:        packet->res1 = DOS_FALSE;
                    940:        packet->res2 = ERROR_NO_FREE_STORE;
                    941:        return;
                    942:     }
                    943: 
                    944:     /* Fixme: may not quite be right */
                    945:     if (0 == stat (k->path, &st)) {
                    946:        if (S_ISDIR (st.st_mode)) {
                    947:            packet->res1 = DOS_FALSE;
                    948:            packet->res2 = ERROR_OBJECT_WRONG_TYPE;
                    949:            return;
                    950:        }
                    951:     }
                    952: 
                    953:     k->fd = open(k->path, mode | O_BINARY, 0777);
                    954: 
                    955:     if(k->fd < 0) {
                    956:        packet->res1 = DOS_FALSE;
                    957:        packet->res2 = dos_errno();
                    958:        free_key(k);
                    959:        return;
                    960:     }
                    961:     success:
                    962:     put_long(fh+36, k->uniq);
                    963: 
                    964:     packet->res1 = DOS_TRUE;
                    965: }
                    966: 
                    967: static void
                    968: action_find_input(Unit* unit, DosPacket* packet)
                    969: {
                    970:     do_find(unit, packet, O_RDWR);
                    971: }
                    972: 
                    973: static void
                    974: action_find_output(Unit* unit, DosPacket* packet)
                    975: {
                    976:     if(unit->ui.readonly) {
                    977:        packet->res1 = DOS_FALSE;
                    978:        packet->res2 = ERROR_DISK_WRITE_PROTECTED;
                    979:        return;
                    980:     }
                    981:     do_find(unit, packet, O_RDWR|O_CREAT|O_TRUNC);
                    982: }
                    983: 
                    984: static void
                    985: action_find_write(Unit* unit, DosPacket* packet)
                    986: {
                    987:     if(unit->ui.readonly) {
                    988:        packet->res1 = DOS_FALSE;
                    989:        packet->res2 = ERROR_DISK_WRITE_PROTECTED;
                    990:        return;
                    991:     }
                    992:     do_find(unit, packet, O_RDWR);
                    993: }
                    994: 
                    995: static void
                    996: action_end(Unit* unit, DosPacket* packet)
                    997: {
                    998:     Key *k;
                    999:     TRACE(("ACTION_END(0x%lx)\n", packet->arg1));
                   1000: 
                   1001:     k = lookup_key(packet->arg1);
                   1002:     free_key(k);
                   1003:     packet->res1 = DOS_TRUE;
                   1004: }
                   1005: 
                   1006: static void
                   1007: action_read(Unit* unit, DosPacket* packet)
                   1008: {
                   1009:     Key *k = lookup_key(packet->arg1);
                   1010:     CPTR addr = packet->arg2;
                   1011:     long size = (LONG)packet->arg3;
                   1012:     int actual;
                   1013: 
                   1014:     TRACE(("ACTION_READ(%s,0x%lx,%ld)\n",k->path,addr,size));
                   1015: #ifdef RELY_ON_LOADSEG_DETECTION
                   1016:     /* HACK HACK HACK HACK 
                   1017:      * Try to detect a LoadSeg() */
                   1018:     if (k->file_pos == 0 && size >= 4) {
                   1019:        unsigned char buf[4];
                   1020:        off_t currpos = lseek(k->fd, 0, SEEK_CUR);
                   1021:        read(k->fd, buf, 4);
                   1022:        lseek(k->fd, currpos, SEEK_SET);
                   1023:        if (buf[0] == 0 && buf[1] == 0 && buf[2] == 3 && buf[3] == 0xF3)
                   1024:            possible_loadseg();
                   1025:     }
                   1026: #endif
                   1027:     if (valid_address (addr, size)) {
                   1028:        UBYTE *realpt;
                   1029:        realpt = get_real_address (addr);
                   1030:        actual = read(k->fd, realpt, size);
                   1031: 
                   1032:        if (actual <= 0) {
                   1033:            packet->res1 = 0;
                   1034:            packet->res2 = dos_errno();
                   1035:        } else {
                   1036:            packet->res1 = actual;
                   1037:            k->file_pos += actual;
                   1038:        }
                   1039:     } else {
                   1040:        char *buf;
                   1041:        fprintf (stderr, "unixfs warning: Bad pointer passed for read\n");
                   1042:        /* ugh this is inefficient but easy */
                   1043:        buf = (char *)malloc(size);
                   1044:        if(!buf) {
                   1045:            packet->res1 = -1;
                   1046:            packet->res2 = ERROR_NO_FREE_STORE;
                   1047:            return;
                   1048:        }
                   1049:        actual = read(k->fd, buf, size);
                   1050: 
                   1051:        if (actual <= 0) {
                   1052:            packet->res1 = 0;
                   1053:            packet->res2 = dos_errno();
                   1054:        } else {
                   1055:            int i;
                   1056:            packet->res1 = actual;
                   1057:            for(i = 0; i < actual; i++)
                   1058:                put_byte(addr + i, buf[i]);
                   1059:            k->file_pos += actual;
                   1060:        }
                   1061:        free(buf);
                   1062:     }
                   1063: }
                   1064: 
                   1065: static void
                   1066: action_write(Unit* unit, DosPacket* packet)
                   1067: {
                   1068:     Key*k = lookup_key(packet->arg1);
                   1069:     CPTR addr = packet->arg2;
                   1070:     long size = packet->arg3;
                   1071:     char *buf;
                   1072:     int i;
                   1073: 
                   1074:     TRACE(("ACTION_WRITE(%s,0x%lx,%ld)\n",k->path,addr,size));
                   1075: 
                   1076:     if(unit->ui.readonly) {
                   1077:        packet->res1 = DOS_FALSE;
                   1078:        packet->res2 = ERROR_DISK_WRITE_PROTECTED;
                   1079:        return;
                   1080:     }
                   1081: 
                   1082:     /* ugh this is inefficient but easy */
                   1083:     buf = (char *)malloc(size);
                   1084:     if(!buf) {
                   1085:        packet->res1 = -1;
                   1086:        packet->res2 = ERROR_NO_FREE_STORE;
                   1087:        return;
                   1088:     }
                   1089: 
                   1090:     for(i = 0; i < size; i++)
                   1091:        buf[i] = get_byte(addr + i);
                   1092: 
                   1093:     packet->res1 = write(k->fd, buf, size);
                   1094:     if(packet->res1 != size)
                   1095:        packet->res2 = dos_errno();
                   1096:     if (packet->res1 >= 0)
                   1097:        k->file_pos += packet->res1;
                   1098: 
                   1099:     free(buf);
                   1100: }
                   1101: 
                   1102: static void
                   1103: action_seek(Unit* unit, DosPacket* packet)
                   1104: {
                   1105:     Key* k = lookup_key(packet->arg1);
                   1106:     long pos = (LONG)packet->arg2;
                   1107:     long mode = (LONG)packet->arg3;
                   1108:     off_t res;
                   1109:     long old;
                   1110:     int whence = SEEK_CUR;
                   1111:     if(mode > 0) whence = SEEK_END;
                   1112:     if(mode < 0) whence = SEEK_SET;
                   1113: 
                   1114:     TRACE(("ACTION_SEEK(%s,%d,%d)\n",k->path,pos,mode));
                   1115: 
                   1116:     old = lseek(k->fd, 0, SEEK_CUR);
                   1117:     res = lseek(k->fd, pos, whence);
                   1118: 
                   1119:     if(-1 == res)
                   1120:        packet->res1 = res;
                   1121:     else
                   1122:        packet->res1 = old;
                   1123:     k->file_pos = res;
                   1124: }
                   1125: 
                   1126: static void
                   1127: action_set_protect(Unit* unit, DosPacket* packet)
                   1128: {
                   1129:     CPTR lock = packet->arg2 << 2;
                   1130:     CPTR name = packet->arg3 << 2;
                   1131:     ULONG mask = packet->arg4;
                   1132:     struct stat statbuf;
                   1133:     mode_t mode;
                   1134:     Key *k;
                   1135: 
                   1136:     TRACE(("ACTION_SET_PROTECT(0x%lx,\"%s\",0x%lx)\n",lock,bstr(name),mask));
                   1137: 
                   1138:     if(unit->ui.readonly) {
                   1139:        packet->res1 = DOS_FALSE;
                   1140:        packet->res2 = ERROR_DISK_WRITE_PROTECTED;
                   1141:        return;
                   1142:     }
                   1143: 
                   1144:     k = make_key(unit, lock, bstr(name));
                   1145:     if(!k) {
                   1146:        packet->res1 = DOS_FALSE;
                   1147:        packet->res2 = ERROR_NO_FREE_STORE;
                   1148:        return;
                   1149:     }
                   1150: 
                   1151:     if(-1 == stat(k->path, &statbuf)) {
                   1152:        free_key(k);
                   1153:        packet->res1 = DOS_FALSE;
                   1154:        packet->res2 = ERROR_OBJECT_NOT_FOUND;
                   1155:        return;
                   1156:     }
                   1157: 
                   1158:     mode = statbuf.st_mode;
                   1159: 
                   1160:     if(mask & (1 << 3))
                   1161:        mode &= ~S_IRUSR;
                   1162:     else
                   1163:        mode |= S_IRUSR;
                   1164: 
                   1165:     if(mask & (1 << 2) || mask & (1 << 0))
                   1166:        mode &= ~S_IWUSR;
                   1167:     else
                   1168:        mode |= S_IWUSR;
                   1169: 
                   1170:     if(mask & (1 << 1))
                   1171:        mode &= ~S_IXUSR;
                   1172:     else
                   1173:        mode |= S_IXUSR;
                   1174: 
                   1175:     if(-1 == chmod(k->path, mode)) {
                   1176:        packet->res1 = DOS_FALSE;
                   1177:        packet->res2 = dos_errno();
                   1178:     } else {
                   1179:        packet->res1 = DOS_TRUE;
                   1180:     }
                   1181:     free_key(k);
                   1182: }
                   1183: 
                   1184: static void
                   1185: action_same_lock(Unit* unit, DosPacket* packet)
                   1186: {
                   1187:     CPTR lock1 = packet->arg1 << 2;
                   1188:     CPTR lock2 = packet->arg2 << 2;
                   1189: 
                   1190:     TRACE(("ACTION_SAME_LOCK(0x%lx,0x%lx)\n",lock1,lock2));
                   1191:     DUMPLOCK(lock1); DUMPLOCK(lock2);
                   1192: 
                   1193:     if(!lock1 || !lock2) {
                   1194:        packet->res1 = (lock1 == lock2) ? DOS_TRUE : DOS_FALSE;
                   1195:     } else {
                   1196:        Key* key1 = lookup_key(get_long(lock1 + 4));
                   1197:        Key* key2 = lookup_key(get_long(lock2 + 4));
                   1198:        packet->res1 = (0 == strcmp(key1->path, key2->path)) ? DOS_TRUE : DOS_FALSE;
                   1199:     }
                   1200: }
                   1201: 
                   1202: static void
                   1203: action_parent(Unit* unit, DosPacket* packet)
                   1204: {
                   1205:     CPTR lock = packet->arg1 << 2;
                   1206:     Key*k;
                   1207: 
                   1208:     TRACE(("ACTION_PARENT(0x%lx)\n",lock));
                   1209: 
                   1210:     if(!lock) {
                   1211:        packet->res1 = 0;
                   1212:        packet->res2 = 0;
                   1213:        return;
                   1214:     }
                   1215: 
                   1216:     k = dup_key(lookup_key(get_long(lock + 4)));
                   1217:     if(0 == strcmp(k->path, unit->ui.rootdir)) {
                   1218:        free_key(k);
                   1219:        packet->res1 = 0;
                   1220:        packet->res2 = 0;
                   1221:        return;
                   1222:     }
                   1223:     {
                   1224:        char *x = strrchr(k->path,'/');
                   1225:        if(!x) { /* ??? This really shouldn't happen! */
                   1226:            free_key(k);
                   1227:            packet->res1 = 0;
                   1228:            packet->res2 = 0;
                   1229:            return;
                   1230:        } else {
                   1231:            *x = '\0';
                   1232:        }
                   1233:     }
                   1234:     packet->res1 = make_lock(unit, k, -2) >> 2;
                   1235: }
                   1236: 
                   1237: static void
                   1238: action_create_dir(Unit* unit, DosPacket* packet)
                   1239: {
                   1240:     CPTR lock = packet->arg1 << 2;
                   1241:     CPTR name = packet->arg2 << 2;
                   1242:     Key* k;
                   1243: 
                   1244:     TRACE(("ACTION_CREATE_DIR(0x%lx,\"%s\")\n",lock,bstr(name)));
                   1245: 
                   1246:     if(unit->ui.readonly) {
                   1247:        packet->res1 = DOS_FALSE;
                   1248:        packet->res2 = ERROR_DISK_WRITE_PROTECTED;
                   1249:        return;
                   1250:     }
                   1251: 
                   1252:     k = make_key(unit, lock, bstr(name));
                   1253: 
                   1254:     if(!k) {
                   1255:        packet->res1 = DOS_FALSE;
                   1256:        packet->res2 = ERROR_NO_FREE_STORE;
                   1257:        return;
                   1258:     }
                   1259: #ifndef AMIGA
                   1260:     if(-1 == mkdir(k->path, 0777)) 
                   1261: #else
                   1262:     if(-1 == mkdir(k->path)) 
                   1263: #endif
                   1264:     {
                   1265:        packet->res1 = DOS_FALSE;
                   1266:        packet->res2 = dos_errno();
                   1267:        free_key(k);
                   1268:        return;
                   1269:     }
                   1270:     packet->res1 = make_lock(unit, k, -2) >> 2;
                   1271: }
                   1272: 
                   1273: static void
                   1274: action_delete_object(Unit* unit, DosPacket* packet)
                   1275: {
                   1276:     CPTR lock = packet->arg1 << 2;
                   1277:     CPTR name = packet->arg2 << 2;
                   1278:     Key* k;
                   1279:     struct stat statbuf;
                   1280: 
                   1281:     TRACE(("ACTION_DELETE_OBJECT(0x%lx,\"%s\")\n",lock,bstr(name)));
                   1282: 
                   1283:     if(unit->ui.readonly) {
                   1284:        packet->res1 = DOS_FALSE;
                   1285:        packet->res2 = ERROR_DISK_WRITE_PROTECTED;
                   1286:        return;
                   1287:     }
                   1288: 
                   1289:     k = make_key(unit, lock, bstr(name));
                   1290: 
                   1291:     if(!k) {
                   1292:        packet->res1 = DOS_FALSE;
                   1293:        packet->res2 = ERROR_NO_FREE_STORE;
                   1294:        return;
                   1295:     }
                   1296:     if(-1 == stat(k->path, &statbuf)) {
                   1297:        packet->res1 = DOS_FALSE;
                   1298:        packet->res2 = dos_errno();
                   1299:        free_key(k);
                   1300:        return;
                   1301:     }
                   1302:     if(S_ISDIR(statbuf.st_mode)) {
                   1303:        if(-1 == rmdir(k->path)) {
                   1304:            packet->res1 = DOS_FALSE;
                   1305:            packet->res2 = dos_errno();
                   1306:            free_key(k);
                   1307:            return;
                   1308:        }
                   1309:     } else {
                   1310:        if(-1 == unlink(k->path)) {
                   1311:            packet->res1 = DOS_FALSE;
                   1312:            packet->res2 = dos_errno();
                   1313:            free_key(k);
                   1314:            return;
                   1315:        }
                   1316:     }
                   1317:     free_key(k);
                   1318:     packet->res1 = DOS_TRUE;
                   1319: }
                   1320: 
                   1321: static void
                   1322: action_set_date(Unit* unit, DosPacket* packet)
                   1323: {
                   1324:     CPTR lock = packet->arg2 << 2;
                   1325:     CPTR name = packet->arg3 << 2;
                   1326:     CPTR date = packet->arg4;
                   1327:     Key* k;
                   1328: #if !defined(AMIGA)
                   1329:     struct utimbuf ut;
                   1330: #endif
                   1331: 
                   1332:     TRACE(("ACTION_SET_DATE(0x%lx,\"%s\")\n",lock,bstr(name)));
                   1333: 
                   1334:     if(unit->ui.readonly) {
                   1335:        packet->res1 = DOS_FALSE;
                   1336:        packet->res2 = ERROR_DISK_WRITE_PROTECTED;
                   1337:        return;
                   1338:     }
                   1339: 
                   1340: #if !defined(AMIGA)
                   1341:     ut.actime = ut.modtime = put_time(get_long(date),get_long(date+4),get_long(date+8));
                   1342: #endif
                   1343:     k = make_key(unit, lock, bstr(name));
                   1344: 
                   1345:     if(!k) {
                   1346:        packet->res1 = DOS_FALSE;
                   1347:        packet->res2 = ERROR_NO_FREE_STORE;
                   1348:        return;
                   1349:     }
                   1350: #if !defined(AMIGA)
                   1351:     if(-1 == utime(k->path, &ut)) {
                   1352:        packet->res1 = DOS_FALSE;
                   1353:        packet->res2 = dos_errno();
                   1354:        free_key(k);
                   1355:        return;
                   1356:     }
                   1357: #endif
                   1358:     free_key(k);
                   1359:     packet->res1 = DOS_TRUE;
                   1360: }
                   1361: 
                   1362: static void
                   1363: action_rename_object(Unit* unit, DosPacket* packet)
                   1364: {
                   1365:     CPTR lock1 = packet->arg1 << 2;
                   1366:     CPTR name1 = packet->arg2 << 2;
                   1367:     Key* k1;
                   1368:     CPTR lock2 = packet->arg3 << 2;
                   1369:     CPTR name2 = packet->arg4 << 2;
                   1370:     Key* k2;
                   1371: 
                   1372:     TRACE(("ACTION_RENAME_OBJECT(0x%lx,\"%s\",",lock1,bstr(name1)));
                   1373:     TRACE(("0x%lx,\"%s\")\n",lock2,bstr(name2)));
                   1374: 
                   1375:     if(unit->ui.readonly) {
                   1376:        packet->res1 = DOS_FALSE;
                   1377:        packet->res2 = ERROR_DISK_WRITE_PROTECTED;
                   1378:        return;
                   1379:     }
                   1380: 
                   1381:     k1 = make_key(unit, lock1, bstr(name1));
                   1382:     if(!k1) {
                   1383:        packet->res1 = DOS_FALSE;
                   1384:        packet->res2 = ERROR_NO_FREE_STORE;
                   1385:        return;
                   1386:     }
                   1387:     k2 = make_key(unit, lock2, bstr(name2));
                   1388:     if(!k2) {
                   1389:        free_key(k1);
                   1390:        packet->res1 = DOS_FALSE;
                   1391:        packet->res2 = ERROR_NO_FREE_STORE;
                   1392:        return;
                   1393:     }
                   1394: 
                   1395:     if(-1 == rename(k1->path, k2->path)) {
                   1396:        packet->res1 = DOS_FALSE;
                   1397:        packet->res2 = dos_errno();
                   1398:        free_key(k1);
                   1399:        free_key(k2);
                   1400:        return;
                   1401:     }
                   1402:     free_key(k1);
                   1403:     free_key(k2);
                   1404:     packet->res1 = DOS_TRUE;
                   1405: }
                   1406: 
                   1407: static void
                   1408: action_current_volume(Unit* unit, DosPacket* packet)
                   1409: {
                   1410:     packet->res1 = unit->volume >> 2;
                   1411: }
                   1412: 
                   1413: static void
                   1414: action_rename_disk(Unit* unit, DosPacket* packet)
                   1415: {
                   1416:     CPTR name = packet->arg1 << 2;
                   1417:     int i;
                   1418:     int namelen;
                   1419: 
                   1420:     TRACE(("ACTION_RENAME_DISK(\"%s\")\n", bstr(name)));
                   1421: 
                   1422:     if(unit->ui.readonly) {
                   1423:        packet->res1 = DOS_FALSE;
                   1424:        packet->res2 = ERROR_DISK_WRITE_PROTECTED;
                   1425:        return;
                   1426:     }
                   1427: 
                   1428:     /* get volume name */
                   1429:     namelen = get_byte(name++);
                   1430:     free(unit->ui.volname);
                   1431:     unit->ui.volname = (char *) malloc(namelen + 1);
                   1432:     for(i = 0; i < namelen; i++)
                   1433:        unit->ui.volname[i] = get_byte(name++);
                   1434:     unit->ui.volname[i] = 0;
                   1435: 
                   1436:     put_byte(unit->volume + 44, namelen);
                   1437:     for(i = 0; i < namelen; i++)
                   1438:        put_byte(unit->volume + 45 + i, unit->ui.volname[i]);
                   1439: 
                   1440:     packet->res1 = DOS_TRUE;
                   1441: }
                   1442: 
                   1443: static void
                   1444: action_is_filesystem(Unit* unit, DosPacket* packet)
                   1445: {
                   1446:     packet->res1 = DOS_TRUE;
                   1447: }
                   1448: 
                   1449: static void
                   1450: action_flush(Unit* unit, DosPacket* packet)
                   1451: {
                   1452:     /* sync(); */ /* pretty drastic, eh */
                   1453:     packet->res1 = DOS_TRUE;
                   1454: }
                   1455: 
                   1456: static ULONG
                   1457: filesys_handler(void)
                   1458: {
                   1459:     DosPacket packet;
                   1460:     Unit *unit = find_unit(regs.a[5]);
                   1461: 
                   1462:     /* got DosPacket in A4 */
                   1463:     packet.addr = regs.a[4];
                   1464:     packet.type = get_long(packet.addr + dp_Type);
                   1465:     packet.res1 = get_long(packet.addr + dp_Res1);
                   1466:     packet.res2 = get_long(packet.addr + dp_Res2);
                   1467:     packet.arg1 = get_long(packet.addr + dp_Arg1);
                   1468:     packet.arg2 = get_long(packet.addr + dp_Arg2);
                   1469:     packet.arg3 = get_long(packet.addr + dp_Arg3);
                   1470:     packet.arg4 = get_long(packet.addr + dp_Arg4);
                   1471: 
                   1472:     if(!unit) {
                   1473:        startup(&packet);
                   1474:        put_long(packet.addr + dp_Res1, packet.res1);
                   1475:        put_long(packet.addr + dp_Res2, packet.res2);
                   1476:        return 0;
                   1477:     }
                   1478: 
                   1479:     if(!unit->volume) {
                   1480:        printf("no volume\n");
                   1481:        return 0;
                   1482:     }
                   1483: 
                   1484:     switch(packet.type) {
                   1485:      case ACTION_LOCATE_OBJECT:
                   1486:        action_lock(unit, &packet);
                   1487:        break;
                   1488: 
                   1489:      case ACTION_FREE_LOCK:
                   1490:        action_free_lock(unit, &packet);
                   1491:        break;
                   1492: 
                   1493:      case ACTION_COPY_DIR:
                   1494:        action_dup_lock(unit, &packet);
                   1495:        break;
                   1496: 
                   1497:      case ACTION_DISK_INFO:
                   1498:        action_disk_info(unit, &packet);
                   1499:        break;
                   1500: 
                   1501:      case ACTION_INFO:
                   1502:        action_info(unit, &packet);
                   1503:        break;
                   1504: 
                   1505:      case ACTION_EXAMINE_OBJECT:
                   1506:        action_examine_object(unit, &packet);
                   1507:        break;
                   1508: 
                   1509:      case ACTION_EXAMINE_NEXT:
                   1510:        action_examine_next(unit, &packet);
                   1511:        break;
                   1512: 
                   1513:      case ACTION_FIND_INPUT:
                   1514:        action_find_input(unit, &packet);
                   1515:        break;
                   1516: 
                   1517:      case ACTION_FIND_WRITE:
                   1518:        action_find_write(unit, &packet);
                   1519:        break;
                   1520: 
                   1521:      case ACTION_FIND_OUTPUT:
                   1522:        action_find_output(unit, &packet);
                   1523:        break;
                   1524: 
                   1525:      case ACTION_END:
                   1526:        action_end(unit, &packet);
                   1527:        break;
                   1528: 
                   1529:      case ACTION_READ:
                   1530:        action_read(unit, &packet);
                   1531:        break;
                   1532: 
                   1533:      case ACTION_WRITE:
                   1534:        action_write(unit, &packet);
                   1535:        break;
                   1536: 
                   1537:      case ACTION_SEEK:
                   1538:        action_seek(unit, &packet);
                   1539:        break;
                   1540: 
                   1541:      case ACTION_SET_PROTECT:
                   1542:        action_set_protect(unit, &packet);
                   1543:        break;
                   1544: 
                   1545:      case ACTION_SAME_LOCK:
                   1546:        action_same_lock(unit, &packet);
                   1547:        break;
                   1548: 
                   1549:      case ACTION_PARENT:
                   1550:        action_parent(unit, &packet);
                   1551:        break;
                   1552: 
                   1553:      case ACTION_CREATE_DIR:
                   1554:        action_create_dir(unit, &packet);
                   1555:        break;
                   1556: 
                   1557:      case ACTION_DELETE_OBJECT:
                   1558:        action_delete_object(unit, &packet);
                   1559:        break;
                   1560: 
                   1561:      case ACTION_RENAME_OBJECT:
                   1562:        action_rename_object(unit, &packet);
                   1563:        break;
                   1564: 
                   1565:      case ACTION_SET_DATE:
                   1566:        action_set_date(unit, &packet);
                   1567:        break;
                   1568: 
                   1569:      case ACTION_CURRENT_VOLUME:
                   1570:        action_current_volume(unit, &packet);
                   1571:        break;
                   1572: 
                   1573:      case ACTION_RENAME_DISK:
                   1574:        action_rename_disk(unit, &packet);
                   1575:        break;
                   1576: 
                   1577:      case ACTION_IS_FILESYSTEM:
                   1578:        action_is_filesystem(unit, &packet);
                   1579:        break;
                   1580: 
                   1581:      case ACTION_FLUSH:
                   1582:        action_flush(unit, &packet);
                   1583:        break;
                   1584: 
                   1585:      default:
                   1586:        TRACE(("*** UNSUPPORTED PACKET %ld\n", packet.type));
                   1587:        packet.res1 = DOS_FALSE;
                   1588:        packet.res2 = ERROR_ACTION_NOT_KNOWN;
                   1589:        break;
                   1590:     }
                   1591: 
                   1592:     put_long(packet.addr + dp_Res1, packet.res1);
                   1593:     put_long(packet.addr + dp_Res2, packet.res2);
                   1594:     TRACE(("reply: %8lx, %ld\n", packet.res1, packet.res2));
                   1595: 
                   1596:     return 0;
                   1597: }
                   1598: 
                   1599: static ULONG filesys_diagentry(void)
                   1600: {
                   1601:     CPTR resaddr = regs.a[2] + 0x10;
                   1602:     
                   1603:     filesys_configdev = regs.a[3];
                   1604:     
                   1605:     if (ROM_hardfile_resid != 0) {
                   1606:        /* Build a struct Resident. This will set up and initialize
                   1607:         * the uae.device */
                   1608:        put_word(resaddr + 0x0, 0x4AFC);
                   1609:        put_long(resaddr + 0x2, resaddr);
                   1610:        put_long(resaddr + 0x6, resaddr + 0x1A); /* Continue scan here */
                   1611:        put_word(resaddr + 0xA, 0x8101); /* RTF_AUTOINIT|RTF_COLDSTART; Version 1 */
                   1612:        put_word(resaddr + 0xC, 0x0305); /* NT_DEVICE; pri 05 */
                   1613:        put_long(resaddr + 0xE, ROM_hardfile_resname);
                   1614:        put_long(resaddr + 0x12, ROM_hardfile_resid);
                   1615:        put_long(resaddr + 0x16, ROM_hardfile_init);
                   1616:     }
                   1617:     resaddr += 0x1A;
                   1618: 
                   1619:     return 1;
                   1620: }
                   1621: 
                   1622: static CPTR build_parmpacket(void)
                   1623: {
                   1624:     CPTR tmp1;
                   1625: 
                   1626:     regs.d[0] = 88; regs.d[1] = 1; /* MEMF_PUBLIC */
                   1627:     tmp1 = CallLib (get_long(4), -198); /* AllocMem() */
                   1628:     if (tmp1 == 0) {
                   1629:        fprintf(stderr, "Not enough memory for filesystem!\n");
                   1630:        return 0;
                   1631:     }
                   1632: 
                   1633:     put_long (tmp1+12, 0); /* Device flags */
                   1634:     put_long (tmp1+16, 16); /* Env. size */
                   1635:     put_long (tmp1+20, 128); /* 512 bytes/block */
                   1636:     put_long (tmp1+24, 0); /* unused */
                   1637:     put_long (tmp1+28, 1); /* heads */
                   1638:     put_long (tmp1+32, 1); /* unused */
                   1639:     put_long (tmp1+36, 32); /* secs per track */
                   1640:     put_long (tmp1+40, 1); /* reserved blocks */
                   1641:     put_long (tmp1+44, 0); /* unused */
                   1642:     put_long (tmp1+48, 0); /* interleave */
                   1643:     put_long (tmp1+52, 0); /* lowCyl */
                   1644: #ifndef __DOS__
                   1645:     put_long (tmp1+56, 511); /* upperCyl */
                   1646: #else
                   1647:     {
                   1648:     extern int numtracks;
                   1649:     put_long (tmp1+56, numtracks-1); /* upperCyl */
                   1650:     }
                   1651: #endif
                   1652:     put_long (tmp1+60, 0); /* Number of buffers */
                   1653:     put_long (tmp1+64, 0); /* Buffer mem type */
                   1654:     put_long (tmp1+68, 0x7FFFFFFF); /* largest transfer */
                   1655:     put_long (tmp1+72, ~1); /* addMask (?) */
                   1656:     put_long (tmp1+76, (ULONG)-1); /* bootPri */
                   1657: #if 0
                   1658:     if (have36)
                   1659:        put_long (tmp1+80, 0x444f5301); /* DOS\1 */
                   1660:     else
                   1661: #endif
                   1662:        put_long (tmp1+80, 0x444f5300); /* DOS\0 */
                   1663: 
                   1664:     put_long (tmp1+84, 0); /* pad */
                   1665:     return tmp1;
                   1666: }
                   1667: 
                   1668: static void make_dev(CPTR param_packet, int unit_no, int is_hardfile, int boot)
                   1669: {
                   1670:     CPTR devicenode, bootnode;
                   1671: 
                   1672:     put_long (param_packet, ui[unit_no].devname_amiga);
                   1673:     put_long (param_packet + 4, is_hardfile ? ROM_hardfile_resname : fsdevname);
                   1674:     put_long (param_packet + 8, ui[unit_no].devno);
                   1675:     
                   1676:     regs.a[0] = param_packet;
                   1677:     devicenode = CallLib (EXPANSION_explibbase, -144); /* MakeDosNode() */
                   1678:     ui[unit_no].startup = get_long(devicenode + 28);
                   1679:     if (!is_hardfile) {
                   1680:        put_long(devicenode+8, 0x0); /* dn_Task */
                   1681:        put_long(devicenode+16, 0x0); /* dn_Handler */
                   1682:        put_long(devicenode+20, 4000); /* dn_StackSize */
                   1683:        put_long(devicenode+32, filesysseglist >> 2); /* dn_SegList */
                   1684:        put_long(devicenode+36, (ULONG)-1); /* dn_GlobalVec */
                   1685:     } else {
                   1686:        /* ??? */
                   1687:        put_long(devicenode+8, 0x0); /* dn_Task */
                   1688:        put_long(devicenode+16, 0x0); /* dn_Handler */
                   1689:        put_long(devicenode+32, 0); /* dn_SegList */
                   1690:     }
                   1691:     
                   1692:     if (boot) {
                   1693:        if (EXPANSION_haveV36) {
                   1694:            regs.d[0] = -1; regs.d[1] = 0;
                   1695:            regs.a[0] = devicenode;
                   1696:            regs.a[1] = filesys_configdev;
                   1697:            CallLib(EXPANSION_explibbase, -36);
                   1698:        } else {
                   1699:            /* Construct a BootNode and Enqueue() it into eb_MountList */
                   1700:            regs.d[0] = 20;
                   1701:            regs.d[1] = 0;
                   1702:            bootnode = CallLib (get_long(4), -198); /* AllocMem() */
                   1703: 
                   1704:            put_word (bootnode + 14, 0);              /* Flags (??) */
                   1705:            put_long (bootnode + 16, devicenode);
                   1706:            put_word (bootnode + 8, 0x10FF-unit_no);          /* Type/Pri */
                   1707:            put_long (bootnode + 10, filesys_configdev); /* Name */
                   1708:            put_long (bootnode + 0, 0);
                   1709:            put_long (bootnode + 4, 0);
                   1710:            regs.a[0] = EXPANSION_explibbase + 74; /* MountList */
                   1711:            regs.a[1] = bootnode;
                   1712:            CallLib (get_long(4), -270); /* Enqueue() */
                   1713:        }
                   1714:     } else {
                   1715:        /* Call AddDosNode() for the constructed node */
                   1716:        regs.a[0] = devicenode;
                   1717:        regs.d[0] = (ULONG)-1;
                   1718:        regs.a[1] = 0;
                   1719:        regs.d[1] = 0;          /* Flags */
                   1720:        CallLib (EXPANSION_explibbase, -150); /* AddDosNode() */
                   1721:     }
                   1722: }
                   1723:     
                   1724: void filesys_init(void)
                   1725: {
                   1726:     int i;
                   1727:     int firstdev = 1;
                   1728:     
                   1729:     /* Open expansion.lib */
                   1730:     
                   1731:     EXPANSION_haveV36 = 0;
                   1732:     regs.d[0] = 36;
                   1733:     regs.a[1] = EXPANSION_explibname;
                   1734:     EXPANSION_explibbase = CallLib (get_long(4), -552); /* OpenLibrary() */
                   1735:     if (EXPANSION_explibbase)
                   1736:        EXPANSION_haveV36 = 1;
                   1737:     else {
                   1738:        regs.d[0] = 0;
                   1739:        regs.a[1] = EXPANSION_explibname;
                   1740:        EXPANSION_explibbase = CallLib (get_long(4), -552); /* OpenLibrary() */
                   1741:     }
                   1742: 
                   1743:     filesys_parampacket = build_parmpacket();
                   1744: 
                   1745:     /* re-use the same parameter packet to make each
                   1746:      * dos node, which will then get tweaked
                   1747:      */
                   1748: 
                   1749:     for(i = 0; i < num_units; i++) {
                   1750:        int is_hardfile = ui[i].volname == NULL;
                   1751:        if (is_hardfile && !EXPANSION_haveV36) {
                   1752:            fprintf(stderr, "Kickstart is older than 2.0, please mount hardfile manually.\n");
                   1753:            continue;
                   1754:        }
                   1755:        make_dev(filesys_parampacket, i, is_hardfile, 1);
                   1756:     }
                   1757: 
                   1758:     regs.a[1] = EXPANSION_explibbase;
                   1759:     CallLib (get_long(4), -414); /* CloseLibrary() */
                   1760:     EXPANSION_explibbase = 0;
                   1761: }
                   1762: 
                   1763: void filesys_install(void)
                   1764: {
                   1765:     int i;
                   1766:     CPTR loop;
                   1767: 
                   1768:     ROM_filesys_resname = ds("UAEunixfs.resource");
                   1769:     ROM_filesys_resid = ds("UAE unixfs 0.2");
                   1770: 
                   1771:     fsdevname = ds("uae.device"); /* does not really exist */
                   1772: 
                   1773:     for(i = 0; i < num_units; i++) {
                   1774:        ui[i].devno = get_new_device(&ui[i].devname, &ui[i].devname_amiga);
                   1775:     }
                   1776: 
                   1777:     ROM_filesys_diagentry = here();
                   1778:     calltrap2(deftrap(filesys_diagentry)); dw(RTS);
                   1779:     
                   1780:     /* align */
                   1781:     align(4);
                   1782:     /* Fake seglist */
                   1783:     dl(16);
                   1784:     filesysseglist = here();
                   1785:     dl(0); /* NextSeg */
                   1786: 
                   1787:     /* start of code */
                   1788: 
                   1789:     /* I don't trust calling functions that Wait() directly,
                   1790:      * so here's a little bit of 68000 code to receive and send our
                   1791:      * DosPackets
                   1792:      */
                   1793:     dw(0x2c79); dl(4);         /* move.l       $4,a6 */
                   1794:     dw(0x2279); dl(0);         /* move.l       0,a1 */
                   1795:     dw(0x4eae); dw(0xfeda);    /* jsr          FindTask(a6) */
                   1796:     dw(0x2040);                        /* move.l       d0,a0 */
                   1797:     dw(0x4be8); dw(0x005c);    /* lea.l        pr_MsgPort(a0),a5 */
                   1798:                                /* loop: */
                   1799:     loop = here();
                   1800:     dw(0x204d);                        /* move.l       a5,a0 */
                   1801:     dw(0x4eae); dw(0xfe80);    /* jsr          WaitPort(a6) */
                   1802:     dw(0x204d);                        /* move.l       a5,a0 */
                   1803:     dw(0x4eae); dw(0xfe8c);    /* jsr          GetMsg(a6) */
                   1804:     dw(0x2840);                        /* move.l       d0,a4 */
                   1805:     dw(0x286c); dw(10);                /* move.l       LN_NAME(a4),a4 */
                   1806:     calltrap2(deftrap(filesys_handler));
                   1807:     dw(0x226c);        dw(0);          /* move.l       dp_Link(a4),a1 */
                   1808:     dw(0x206c); dw(4);         /* move.l       dp_Port(a4),a0 */
                   1809:     dw(0x294d); dw(4);         /* move.l       a5,dp_Port(a4) */
                   1810:     dw(0x4eae); dw(0xfe92);    /* jsr          PutMsg(a6) */
                   1811:     dw(0x4ef9); dl(loop);      /* jmp          loop */
                   1812: 
                   1813: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.