OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [fs/] [romfs/] [inode.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1628 jcastillo
/*
2
 * ROMFS file system, Linux implementation
3
 *
4
 * Copyright (C) 1997  Janos Farkas <chexum@shadow.banki.hu>
5
 *
6
 * Using parts of the minix filesystem
7
 * Copyright (C) 1991, 1992  Linus Torvalds
8
 *
9
 * and parts of the affs filesystem additionally
10
 * Copyright (C) 1993  Ray Burr
11
 * Copyright (C) 1996  Hans-Joachim Widmaier
12
 *
13
 * This program is free software; you can redistribute it and/or
14
 * modify it under the terms of the GNU General Public License
15
 * as published by the Free Software Foundation; either version
16
 * 2 of the License, or (at your option) any later version.
17
 *
18
 * Changes
19
 *                                      Changed for 2.1.19 modules
20
 *      Jan 1997                        Initial release
21
 *      Nov 1997        2.0.32          Merging applicable 2.1 bugfixes
22
 *
23
 */
24
 
25
/* todo:
26
 *      - see Documentation/filesystems/romfs.txt
27
 *      - use malloced memory for file names?
28
 *      - quicklist routines from fs/namei.c, get_page is possibly not
29
 *        intended to be used now
30
 *      - considering write access...
31
 *      - network (tftp) files?
32
 *      - in the ancient times something leaked to made umounts
33
 *        impossible, but I've not seen it in the last months
34
 */
35
 
36
/*
37
 * Sorry about some optimizations and for some goto's.  I just wanted
38
 * to squeeze some more bytes out of this code.. :)
39
 */
40
 
41
#include <linux/module.h>
42
#include <linux/types.h>
43
#include <linux/errno.h>
44
#include <linux/malloc.h>
45
#include <linux/romfs_fs.h>
46
#include <linux/fs.h>
47
#include <linux/locks.h>
48
 
49
#include <asm/byteorder.h>
50
 
51
static int inline min(int a, int b)
52
{
53
        return a<b ? a : b;
54
}
55
 
56
static __s32
57
romfs_checksum(void *data, int size)
58
{
59
        __s32 sum, *ptr;
60
 
61
        sum = 0; ptr = data;
62
        size>>=2;
63
        while (size>0) {
64
                sum += ntohl(*ptr++);
65
                size--;
66
        }
67
        return sum;
68
}
69
 
70
static struct super_operations romfs_ops;
71
 
72
static struct super_block *
73
romfs_read_super(struct super_block *s, void *data, int silent)
74
{
75
        struct buffer_head *bh;
76
        kdev_t dev = s->s_dev;
77
        struct romfs_super_block *rsb;
78
        int sz;
79
 
80
        MOD_INC_USE_COUNT;
81
 
82
        /* I would parse the options here, but there are none.. :) */
83
 
84
        lock_super(s);
85
        set_blocksize(dev, ROMBSIZE);
86
        s->s_blocksize = ROMBSIZE;
87
        s->s_blocksize_bits = ROMBSBITS;
88
        bh = bread(dev, 0, ROMBSIZE);
89
        if (!bh) {
90
                /* XXX merge with other printk? */
91
                printk ("romfs: unable to read superblock\n");
92
                goto outnobh;
93
        }
94
 
95
        rsb = (struct romfs_super_block *)bh->b_data;
96
        sz = ntohl(rsb->size);
97
        if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1
98
           || sz < ROMFH_SIZE) {
99
                if (!silent)
100
                        printk ("VFS: Can't find a romfs filesystem on dev "
101
                                "%s.\n", kdevname(dev));
102
                goto out;
103
        }
104
        if (romfs_checksum(rsb, min(sz,512))) {
105
                printk ("romfs: bad initial checksum on dev "
106
                        "%s.\n", kdevname(dev));
107
                goto out;
108
        }
109
 
110
        s->s_magic = ROMFS_MAGIC;
111
        s->u.romfs_sb.s_maxsize = sz;
112
 
113
        s->s_flags |= MS_RDONLY;
114
 
115
        /* Find the start of the fs */
116
        sz = (ROMFH_SIZE +
117
              strnlen(rsb->name, ROMFS_MAXFN) + 1 + ROMFH_PAD)
118
             & ROMFH_MASK;
119
 
120
        brelse(bh);
121
 
122
        s->s_op = &romfs_ops;
123
 
124
        if (!(s->s_mounted = iget(s, sz)))
125
                goto outnobh;
126
 
127
        unlock_super(s);
128
 
129
        /* Ehrhm; sorry.. :)  And thanks to Hans-Joachim Widmaier  :) */
130
        if (0) {
131
out:
132
                brelse(bh);
133
outnobh:
134
                s->s_dev = 0;
135
                unlock_super(s);
136
                MOD_DEC_USE_COUNT;
137
                s = NULL;
138
        }
139
 
140
        return s;
141
}
142
 
143
/* Nothing to do.. */
144
 
145
static void
146
romfs_put_super(struct super_block *sb)
147
{
148
        lock_super(sb);
149
        sb->s_dev = 0;
150
        unlock_super(sb);
151
        MOD_DEC_USE_COUNT;
152
        return;
153
}
154
 
155
/* That's simple too. */
156
 
157
static void
158
romfs_statfs(struct super_block *sb, struct statfs *buf, int bufsize)
159
{
160
        struct statfs tmp;
161
 
162
        memset(&tmp, 0, sizeof(tmp));
163
        tmp.f_type = ROMFS_MAGIC;
164
        tmp.f_bsize = ROMBSIZE;
165
        tmp.f_blocks = (sb->u.romfs_sb.s_maxsize+ROMBSIZE-1)>>ROMBSBITS;
166
        memcpy_tofs(buf, &tmp, bufsize);
167
}
168
 
169
/* some helper routines */
170
 
171
static int
172
romfs_strnlen(struct inode *i, unsigned long offset, unsigned long count)
173
{
174
        struct buffer_head *bh;
175
        unsigned long avail, maxsize, res;
176
 
177
        maxsize = i->i_sb->u.romfs_sb.s_maxsize;
178
        if (offset >= maxsize)
179
                return -1;
180
 
181
        /* strnlen is almost always valid */
182
        if (count > maxsize || offset+count > maxsize)
183
                count = maxsize-offset;
184
 
185
        bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
186
        if (!bh)
187
                return -1;              /* error */
188
 
189
        avail = ROMBSIZE - (offset & ROMBMASK);
190
        maxsize = min(count, avail);
191
        res = strnlen(((char *)bh->b_data)+(offset&ROMBMASK), maxsize);
192
        brelse(bh);
193
 
194
        if (res < maxsize)
195
                return res;             /* found all of it */
196
 
197
        while (res < count) {
198
                offset += maxsize;
199
 
200
                bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
201
                if (!bh)
202
                        return -1;
203
                maxsize = min(count-res, ROMBSIZE);
204
                avail = strnlen(bh->b_data, maxsize);
205
                res += avail;
206
                brelse(bh);
207
                if (avail < maxsize)
208
                        return res;
209
        }
210
        return res;
211
}
212
 
213
static int
214
romfs_copyfrom(struct inode *i, void *dest, unsigned long offset, unsigned long count)
215
{
216
        struct buffer_head *bh;
217
        unsigned long avail, maxsize, res;
218
 
219
        maxsize = i->i_sb->u.romfs_sb.s_maxsize;
220
        if (offset >= maxsize || count > maxsize || offset+count>maxsize)
221
                return -1;
222
 
223
        bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
224
        if (!bh)
225
                return -1;              /* error */
226
 
227
        avail = ROMBSIZE - (offset & ROMBMASK);
228
        maxsize = min(count, avail);
229
        memcpy(dest, ((char *)bh->b_data) + (offset & ROMBMASK), maxsize);
230
        brelse(bh);
231
 
232
        res = maxsize;                  /* all of it */
233
 
234
        while (res < count) {
235
                offset += maxsize;
236
                dest += maxsize;
237
 
238
                bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
239
                if (!bh)
240
                        return -1;
241
                maxsize = min(count-res, ROMBSIZE);
242
                memcpy(dest, bh->b_data, maxsize);
243
                brelse(bh);
244
                res += maxsize;
245
        }
246
        return res;
247
}
248
 
249
/* Directory operations */
250
 
251
static int
252
romfs_readdir(struct inode *i, struct file *filp, void *dirent, filldir_t filldir)
253
{
254
        struct romfs_inode ri;
255
        unsigned long offset, maxoff;
256
        int j, ino, nextfh;
257
        int stored = 0;
258
        char fsname[ROMFS_MAXFN];       /* XXX dynamic? */
259
 
260
        if (!i || !S_ISDIR(i->i_mode))
261
                return -EBADF;
262
 
263
        maxoff = i->i_sb->u.romfs_sb.s_maxsize;
264
 
265
        offset = filp->f_pos;
266
        if (!offset) {
267
                offset = i->i_ino & ROMFH_MASK;
268
                if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0)
269
                        return stored;
270
                offset = ntohl(ri.spec) & ROMFH_MASK;
271
        }
272
 
273
        /* Not really failsafe, but we are read-only... */
274
        for(;;) {
275
                if (!offset || offset >= maxoff) {
276
                        offset = 0xffffffff;
277
                        filp->f_pos = offset;
278
                        return stored;
279
                }
280
                filp->f_pos = offset;
281
 
282
                /* Fetch inode info */
283
                if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0)
284
                        return stored;
285
 
286
                j = romfs_strnlen(i, offset+ROMFH_SIZE, sizeof(fsname)-1);
287
                if (j < 0)
288
                        return stored;
289
 
290
                fsname[j]=0;
291
                romfs_copyfrom(i, fsname, offset+ROMFH_SIZE, j);
292
 
293
                ino = offset;
294
                nextfh = ntohl(ri.next);
295
                if ((nextfh & ROMFH_TYPE) == ROMFH_HRD)
296
                        ino = ntohl(ri.spec);
297
                if (filldir(dirent, fsname, j, offset, ino) < 0) {
298
                        return stored;
299
                }
300
                stored++;
301
                offset = nextfh & ROMFH_MASK;
302
        }
303
}
304
 
305
static int
306
romfs_lookup(struct inode *dir, const char *name, int len, struct inode **result)
307
{
308
        unsigned long offset, maxoff;
309
        int fslen, res;
310
        char fsname[ROMFS_MAXFN];       /* XXX dynamic? */
311
        struct romfs_inode ri;
312
 
313
        *result = NULL;
314
        if (!dir || !S_ISDIR(dir->i_mode)) {
315
                res = -EBADF;
316
                goto out;
317
        }
318
 
319
        offset = dir->i_ino & ROMFH_MASK;
320
        if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) {
321
                res = -ENOENT;
322
                goto out;
323
        }
324
 
325
        maxoff = dir->i_sb->u.romfs_sb.s_maxsize;
326
        offset = ntohl(ri.spec) & ROMFH_MASK;
327
 
328
        for(;;) {
329
                if (!offset || offset >= maxoff
330
                    || romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) {
331
                        res = -ENOENT;
332
                        goto out;
333
                }
334
 
335
                /* try to match the first 16 bytes of name */
336
                fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, ROMFH_SIZE);
337
                if (len < ROMFH_SIZE) {
338
                        if (len == fslen) {
339
                                /* both are shorter, and same size */
340
                                romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
341
                                if (strncmp (name, fsname, len) == 0)
342
                                        break;
343
                        }
344
                } else if (fslen >= ROMFH_SIZE) {
345
                        /* both are longer; XXX optimize max size */
346
                        fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, sizeof(fsname)-1);
347
                        if (len == fslen) {
348
                                romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
349
                                if (strncmp(name, fsname, len) == 0)
350
                                        break;
351
                        }
352
                }
353
                /* next entry */
354
                offset = ntohl(ri.next) & ROMFH_MASK;
355
        }
356
 
357
        /* Hard link handling */
358
        if ((ntohl(ri.next) & ROMFH_TYPE) == ROMFH_HRD)
359
                offset = ntohl(ri.spec) & ROMFH_MASK;
360
 
361
        res = 0;
362
        if (!(*result = iget(dir->i_sb, offset)))
363
                res = -EACCES;
364
 
365
out:
366
        iput(dir);
367
        return res;
368
}
369
 
370
/*
371
 * Ok, we do readpage, to be able to execute programs.  Unfortunately,
372
 * we can't use bmap, since we have looser alignments.
373
 */
374
 
375
static int
376
romfs_readpage(struct inode * inode, struct page * page)
377
{
378
        unsigned long buf;
379
        unsigned long offset, avail, readlen;
380
        int result = -EIO;
381
 
382
        page->count++;
383
        set_bit(PG_locked, &page->flags);
384
 
385
        buf = page_address(page);
386
        clear_bit(PG_uptodate, &page->flags);
387
        clear_bit(PG_error, &page->flags);
388
        offset = page->offset;
389
        if (offset < inode->i_size) {
390
                avail = inode->i_size-offset;
391
                readlen = min(avail, PAGE_SIZE);
392
                if (romfs_copyfrom(inode, (void *)buf, inode->u.romfs_i.i_dataoffset+offset, readlen) == readlen) {
393
                        if (readlen < PAGE_SIZE) {
394
                                memset((void *)(buf+readlen),0,PAGE_SIZE-readlen);
395
                        }
396
                        set_bit(PG_uptodate, &page->flags);
397
                        result = 0;
398
                }
399
        }
400
        if (result) {
401
                set_bit(PG_error, &page->flags);
402
                memset((void *)buf, 0, PAGE_SIZE);
403
        }
404
 
405
        clear_bit(PG_locked, &page->flags);
406
        wake_up(&page->wait);
407
        free_page(buf);
408
 
409
        return result;
410
}
411
 
412
#ifdef MAGIC_ROM_PTR
413
static int
414
romfs_romptr(struct inode * inode, struct file * filp, struct vm_area_struct * vma)
415
{
416
        vma->vm_offset += inode->u.romfs_i.i_dataoffset;
417
 
418
        if ((vma->vm_flags & VM_WRITE) || bromptr(inode->i_dev, vma))
419
                return -ENOSYS;
420
 
421
        return 0;
422
}
423
#endif
424
 
425
static int
426
romfs_readlink(struct inode *inode, char *buffer, int len)
427
{
428
        int mylen;
429
        char buf[ROMFS_MAXFN];          /* XXX dynamic */
430
 
431
        if (!inode || !S_ISLNK(inode->i_mode)) {
432
                mylen = -EBADF;
433
                goto out;
434
        }
435
 
436
        mylen = min(sizeof(buf), inode->i_size);
437
 
438
        if (romfs_copyfrom(inode, buf, inode->u.romfs_i.i_dataoffset, mylen) <= 0) {
439
                mylen = -EIO;
440
                goto out;
441
        }
442
        memcpy_tofs(buffer, buf, mylen);
443
 
444
out:
445
        iput(inode);
446
        return mylen;
447
}
448
 
449
static int
450
romfs_follow_link(struct inode *dir, struct inode *inode,
451
        int flag, int mode, struct inode **res_inode)
452
{
453
        int error, len;
454
        char *buf;
455
 
456
        *res_inode = NULL;
457
        if (!dir) {
458
                dir = current->fs->root;
459
                dir->i_count++;
460
        }
461
 
462
        if (!inode) {
463
                iput(dir);
464
                return -ENOENT;
465
        }
466
        if (!S_ISLNK(inode->i_mode)) {
467
                *res_inode = inode;
468
                iput(dir);
469
                return 0;
470
        }
471
        if (current->link_count > 5) {
472
                iput(inode);
473
                iput(dir);
474
                return -ELOOP;
475
        }
476
 
477
        /* Eek. Short enough. */
478
        len = inode->i_size;
479
        if (!(buf = kmalloc(len+1, GFP_KERNEL))) {
480
                iput(inode);
481
                iput(dir);
482
                /* correct?  spin? */
483
                return -EAGAIN;
484
        }
485
        error = romfs_copyfrom(inode, buf, inode->u.romfs_i.i_dataoffset, len);
486
        if (error != len) {
487
                iput(inode);
488
                iput(dir);
489
                error = -EIO;
490
        } else {
491
                iput(inode);
492
                buf[len] = 0;
493
                current->link_count++;
494
                error = open_namei(buf, flag, mode, res_inode, dir);
495
                current->link_count--;
496
        }
497
 
498
        kfree(buf);
499
        return error;
500
}
501
 
502
/* Mapping from our types to the kernel */
503
 
504
static struct file_operations romfs_file_operations = {
505
        NULL,                   /* lseek - default */
506
        generic_file_read,      /* read */
507
        NULL,                   /* write - bad */
508
        NULL,                   /* readdir */
509
        NULL,                   /* select - default */
510
        NULL,                   /* ioctl */
511
        generic_file_mmap,      /* mmap */
512
        NULL,                   /* open */
513
        NULL,                   /* release */
514
        NULL,                   /* fsync */
515
        NULL,                   /* fasync */
516
        NULL,                   /* check_media_change */
517
        NULL,                   /* revalidate */
518
#ifdef MAGIC_ROM_PTR
519
        romfs_romptr,           /* romptr */
520
#endif
521
};
522
 
523
static struct inode_operations romfs_file_inode_operations = {
524
        &romfs_file_operations,
525
        NULL,                   /* create */
526
        NULL,                   /* lookup */
527
        NULL,                   /* link */
528
        NULL,                   /* unlink */
529
        NULL,                   /* symlink */
530
        NULL,                   /* mkdir */
531
        NULL,                   /* rmdir */
532
        NULL,                   /* mknod */
533
        NULL,                   /* rename */
534
        NULL,                   /* readlink */
535
        NULL,                   /* follow_link */
536
        romfs_readpage,         /* readpage */
537
        NULL,                   /* writepage */
538
        NULL,                   /* bmap -- not really */
539
        NULL,                   /* truncate */
540
        NULL,                   /* permission */
541
        NULL,                   /* smap */
542
};
543
 
544
static struct file_operations romfs_dir_operations = {
545
        NULL,                   /* lseek - default */
546
        NULL,                   /* read */
547
        NULL,                   /* write - bad */
548
        romfs_readdir,          /* readdir */
549
        NULL,                   /* select - default */
550
        NULL,                   /* ioctl */
551
        NULL,                   /* mmap */
552
        NULL,                   /* open */
553
        NULL,                   /* release */
554
        NULL,                   /* fsync */
555
        NULL,                   /* fasync */
556
        NULL,                   /* check_media_change */
557
        NULL                    /* revalidate */
558
};
559
 
560
/* Merged dir/symlink op table.  readdir/lookup/readlink/follow_link
561
 * will protect from type mismatch.
562
 */
563
 
564
static struct inode_operations romfs_dirlink_inode_operations = {
565
        &romfs_dir_operations,
566
        NULL,                   /* create */
567
        romfs_lookup,           /* lookup */
568
        NULL,                   /* link */
569
        NULL,                   /* unlink */
570
        NULL,                   /* symlink */
571
        NULL,                   /* mkdir */
572
        NULL,                   /* rmdir */
573
        NULL,                   /* mknod */
574
        NULL,                   /* rename */
575
        romfs_readlink,         /* readlink */
576
        romfs_follow_link,      /* follow_link */
577
        NULL,                   /* readpage */
578
        NULL,                   /* writepage */
579
        NULL,                   /* bmap */
580
        NULL,                   /* truncate */
581
        NULL,                   /* permission */
582
        NULL,                   /* smap */
583
};
584
 
585
static mode_t romfs_modemap[] =
586
{
587
        0, S_IFDIR, S_IFREG, S_IFLNK+0777,
588
        S_IFBLK, S_IFCHR, S_IFSOCK, S_IFIFO
589
};
590
 
591
static struct inode_operations *romfs_inoops[] =
592
{
593
        NULL,                           /* hardlink, handled elsewhere */
594
        &romfs_dirlink_inode_operations,
595
        &romfs_file_inode_operations,
596
        &romfs_dirlink_inode_operations,
597
        &blkdev_inode_operations,       /* standard handlers */
598
        &chrdev_inode_operations,
599
        NULL,                           /* socket */
600
        NULL,                           /* fifo */
601
};
602
 
603
static void
604
romfs_read_inode(struct inode *i)
605
{
606
        int nextfh, ino;
607
        struct romfs_inode ri;
608
 
609
        ino = i->i_ino & ROMFH_MASK;
610
 
611
        i->i_op = NULL;
612
        i->i_mode = 0;
613
 
614
        /* Loop for finding the real hard link */
615
        for(;;) {
616
                if (romfs_copyfrom(i, &ri, ino, ROMFH_SIZE) <= 0) {
617
                        printk("romfs: read error for inode 0x%x\n", ino);
618
                        return;
619
                }
620
                /* XXX: do romfs_checksum here too (with name) */
621
 
622
                nextfh = ntohl(ri.next);
623
                if ((nextfh & ROMFH_TYPE) != ROMFH_HRD)
624
                        break;
625
 
626
                ino = ntohl(ri.spec) & ROMFH_MASK;
627
        }
628
 
629
        i->i_nlink = 1;         /* Hard to decide.. */
630
        i->i_size = ntohl(ri.size);
631
        i->i_mtime = i->i_atime = i->i_ctime = 0;
632
        i->i_uid = i->i_gid = 0;
633
 
634
        i->i_op = romfs_inoops[nextfh & ROMFH_TYPE];
635
 
636
        /* Precalculate the data offset */
637
        ino = romfs_strnlen(i, ino+ROMFH_SIZE, ROMFS_MAXFN);
638
        if (ino >= 0)
639
                ino = ((ROMFH_SIZE+ino+1+ROMFH_PAD)&ROMFH_MASK);
640
        else
641
                ino = 0;
642
 
643
        i->u.romfs_i.i_metasize = ino;
644
        i->u.romfs_i.i_dataoffset = ino+(i->i_ino&ROMFH_MASK);
645
 
646
        /* Compute permissions */
647
        ino = S_IRUGO|S_IWUSR;
648
        ino |= romfs_modemap[nextfh & ROMFH_TYPE];
649
        if (nextfh & ROMFH_EXEC) {
650
                ino |= S_IXUGO;
651
        }
652
        i->i_mode = ino;
653
 
654
        if (S_ISFIFO(ino))
655
                init_fifo(i);
656
        else if (S_ISDIR(ino))
657
                i->i_size = i->u.romfs_i.i_metasize;
658
        else if (S_ISBLK(ino) || S_ISCHR(ino)) {
659
                i->i_mode &= ~(S_IRWXG|S_IRWXO);
660
                ino = ntohl(ri.spec);
661
                i->i_rdev = MKDEV(ino>>16,ino&0xffff);
662
        }
663
}
664
 
665
static struct super_operations romfs_ops = {
666
        romfs_read_inode,       /* read inode */
667
        NULL,                   /* notify change */
668
        NULL,                   /* write inode */
669
        NULL,                   /* put inode */
670
        romfs_put_super,        /* put super */
671
        NULL,                   /* write super */
672
        romfs_statfs,           /* statfs */
673
        NULL                    /* remount */
674
};
675
 
676
static struct file_system_type romfs_fs_type = {
677
        romfs_read_super, "romfs", 1, NULL
678
};
679
 
680
int
681
init_romfs_fs(void)
682
{
683
        return register_filesystem(&romfs_fs_type);
684
}
685
 
686
#ifdef MODULE
687
 
688
/* Yes, works even as a module... :) */
689
 
690
int
691
init_module(void)
692
{
693
        int status;
694
 
695
        if ((status = init_romfs_fs()) == 0)
696
                register_symtab(0);
697
        return status;
698
}
699
 
700
void
701
cleanup_module(void)
702
{
703
        unregister_filesystem(&romfs_fs_type);
704
}
705
#endif

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.