|
|
1.1 root 1: /*
2:
3: Copyright 1992 Eric R. Smith. All rights reserved.
4:
5: */
6:
7:
8:
9: /* Shared memory file system */
10:
11:
12:
13: #include "mint.h"
14:
15:
16:
17:
18:
19: static long shm_root P_((int drv, fcookie *fc));
20:
21: static long shm_creat P_((fcookie *dir, const char *name, unsigned mode,
22:
23: int attrib, fcookie *fc));
24:
25: static long shm_lookup P_((fcookie *dir, const char *name, fcookie *fc));
26:
27: static long shm_getxattr P_((fcookie *fc, XATTR *xattr));
28:
29: static long shm_chattr P_((fcookie *fc, int attrib));
30:
31: static long shm_chown P_((fcookie *fc, int uid, int gid));
32:
33: static long shm_chmode P_((fcookie *fc, unsigned mode));
34:
35: static long shm_rmdir P_((fcookie *dir, const char *name));
36:
37: static long shm_remove P_((fcookie *dir, const char *name));
38:
39: static long shm_getname P_((fcookie *root, fcookie *dir, char *pathname));
40:
41: static long shm_rename P_((fcookie *olddir, char *oldname,
42:
43: fcookie *newdir, const char *newname));
44:
45: static long shm_opendir P_((DIR *dirh, int flags));
46:
47: static long shm_readdir P_((DIR *dirh, char *nm, int nmlen, fcookie *));
48:
49: static long shm_rewinddir P_((DIR *dirh));
50:
51: static long shm_closedir P_((DIR *dirh));
52:
53: static long shm_pathconf P_((fcookie *dir, int which));
54:
55: static long shm_dfree P_((fcookie *dir, long *buf));
56:
57: static DEVDRV * shm_getdev P_((fcookie *fc, long *devsp));
58:
59:
60:
61: static long shm_open P_((FILEPTR *f));
62:
63: static long shm_write P_((FILEPTR *f, const char *buf, long bytes));
64:
65: static long shm_read P_((FILEPTR *f, char *buf, long bytes));
66:
67: static long shm_lseek P_((FILEPTR *f, long where, int whence));
68:
69: static long shm_ioctl P_((FILEPTR *f, int mode, void *buf));
70:
71: static long shm_datime P_((FILEPTR *f, short *time, int rwflag));
72:
73: static long shm_close P_((FILEPTR *f, int pid));
74:
75:
76:
77: /* dummy routines from biosfs.c */
78:
79: extern long null_select P_((FILEPTR *f, long p, int mode));
80:
81: extern void null_unselect P_((FILEPTR *f, long p, int mode));
82:
83:
84:
85: static short shmtime, shmdate;
86:
87:
88:
89: #define SHMNAME_MAX 15
90:
91:
92:
93: typedef struct shmfile {
94:
95: struct shmfile *next;
96:
97: char filename[SHMNAME_MAX+1];
98:
99: int uid, gid;
100:
101: short time, date;
102:
103: unsigned mode;
104:
105: int inuse;
106:
107: MEMREGION *reg;
108:
109: } SHMFILE;
110:
111:
112:
113: SHMFILE *shmroot = 0;
114:
115:
116:
117: DEVDRV shm_device = {
118:
119: shm_open, shm_write, shm_read, shm_lseek, shm_ioctl, shm_datime,
120:
121: shm_close, null_select, null_unselect
122:
123: };
124:
125:
126:
127: FILESYS shm_filesys = {
128:
129: (FILESYS *)0,
130:
131: 0,
132:
133: shm_root,
134:
135: shm_lookup, shm_creat, shm_getdev, shm_getxattr,
136:
137: shm_chattr, shm_chown, shm_chmode,
138:
139: nomkdir, shm_rmdir, shm_remove, shm_getname, shm_rename,
140:
141: shm_opendir, shm_readdir, shm_rewinddir, shm_closedir,
142:
143: shm_pathconf, shm_dfree,
144:
145: nowritelabel, noreadlabel, nosymlink, noreadlink, nohardlink,
146:
147: nofscntl, nodskchng
148:
149: };
150:
151:
152:
153: long
154:
155: shm_root(drv, fc)
156:
157: int drv;
158:
159: fcookie *fc;
160:
161: {
162:
163: if (drv == SHMDEVICE) {
164:
165: fc->fs = &shm_filesys;
166:
167: fc->dev = drv;
168:
169: fc->index = 0L;
170:
171: return 0;
172:
173: }
174:
175: fc->fs = 0;
176:
177: return EINTRN;
178:
179: }
180:
181:
182:
183: static long
184:
185: shm_lookup(dir, name, fc)
186:
187: fcookie *dir;
188:
189: const char *name;
190:
191: fcookie *fc;
192:
193: {
194:
195: SHMFILE *s;
196:
197:
198:
199: if (dir->index != 0) {
200:
201: DEBUG("shm_lookup: bad directory");
202:
203: return EPTHNF;
204:
205: }
206:
207:
208:
209: /* special case: an empty name in a directory means that directory */
210:
211: /* so does "." */
212:
213: if (!*name || (name[0] == '.' && name[1] == 0)) {
214:
215: *fc = *dir;
216:
217: return 0;
218:
219: }
220:
221:
222:
223: /* another special case: ".." could be a mount point */
224:
225: if (!strcmp(name, "..")) {
226:
227: *fc = *dir;
228:
229: return EMOUNT;
230:
231: }
232:
233:
234:
235: for (s = shmroot; s; s = s->next) {
236:
237: if (!stricmp(s->filename,name))
238:
239: break;
240:
241: }
242:
243:
244:
245: if (!s) {
246:
247: DEBUG("shm_lookup: name not found");
248:
249: return EFILNF;
250:
251: } else {
252:
253: fc->index = (long)s;
254:
255: fc->fs = &shm_filesys;
256:
257: fc->dev = SHMDEVICE;
258:
259: }
260:
261: return 0;
262:
263: }
264:
265:
266:
267: static long
268:
269: shm_getxattr(fc, xattr)
270:
271: fcookie *fc;
272:
273: XATTR *xattr;
274:
275: {
276:
277: SHMFILE *s;
278:
279:
280:
281: xattr->blksize = 1;
282:
283: if (fc->index == 0) {
284:
285: /* the root directory */
286:
287: xattr->index = 0;
288:
289: xattr->dev = SHMDEVICE;
290:
291: xattr->nlink = 1;
292:
293: xattr->uid = xattr->gid = 0;
294:
295: xattr->size = xattr->nblocks = 0;
296:
297: xattr->mtime = xattr->atime = xattr->ctime = shmtime;
298:
299: xattr->mdate = xattr->adate = xattr->cdate = shmdate;
300:
301: xattr->mode = S_IFDIR | DEFAULT_DIRMODE;
302:
303: xattr->attr = FA_DIR;
304:
305: return 0;
306:
307: }
308:
309:
310:
311: s = (SHMFILE *)fc->index;
312:
313: xattr->index = (long) s;
314:
315: xattr->dev = SHMDEVICE;
316:
317: xattr->nlink = 1;
318:
319: xattr->uid = s->uid; xattr->gid = s->gid;
320:
321: if (s->reg)
322:
323: xattr->size = xattr->nblocks = s->reg->len;
324:
325: else
326:
327: xattr->size = xattr->nblocks = 0;
328:
329:
330:
331: xattr->mtime = xattr->ctime = xattr->atime = s->time;
332:
333: xattr->mdate = xattr->cdate = xattr->adate = s->date;
334:
335: xattr->mode = S_IMEM | S_IRUSR | S_IWUSR;
336:
337: xattr->attr = 0;
338:
339: return 0;
340:
341: }
342:
343:
344:
345: static long
346:
347: shm_chattr(fc, attrib)
348:
349: fcookie *fc;
350:
351: int attrib;
352:
353: {
354:
355: return 0;
356:
357: }
358:
359:
360:
361: static long
362:
363: shm_chown(fc, uid, gid)
364:
365: fcookie *fc;
366:
367: int uid, gid;
368:
369: {
370:
371: SHMFILE *s;
372:
373:
374:
375: s = (SHMFILE *)fc->index;
376:
377: if (!s)
378:
379: return EINVFN;
380:
381: s->uid = uid;
382:
383: s->gid = gid;
384:
385: return 0;
386:
387: }
388:
389:
390:
391: static long
392:
393: shm_chmode(fc, mode)
394:
395: fcookie *fc;
396:
397: unsigned mode;
398:
399: {
400:
401: SHMFILE *s;
402:
403:
404:
405: s = (SHMFILE *)fc->index;
406:
407: if (!s)
408:
409: return EINVFN;
410:
411: s->mode = mode;
412:
413: return 0;
414:
415: }
416:
417:
418:
419: static long
420:
421: shm_rmdir(dir, name)
422:
423: fcookie *dir;
424:
425: const char *name;
426:
427: {
428:
429: return EPTHNF;
430:
431: }
432:
433:
434:
435: static long
436:
437: shm_remove(dir, name)
438:
439: fcookie *dir;
440:
441: const char *name;
442:
443: {
444:
445: SHMFILE *s, **old;
446:
447:
448:
449: if (dir->index != 0)
450:
451: return EPTHNF;
452:
453:
454:
455: old = &shmroot;
456:
457: for (s = shmroot; s; s = s->next) {
458:
459: if (!stricmp(s->filename, name))
460:
461: break;
462:
463: old = &s->next;
464:
465: }
466:
467: if (!s)
468:
469: return EFILNF;
470:
471: if (s->inuse)
472:
473: return EACCDN;
474:
475: *old = s->next;
476:
477:
478:
479: s->reg->links--;
480:
481: if (s->reg->links <= 0) {
482:
483: free_region(s->reg);
484:
485: }
486:
487: kfree(s);
488:
489: shmtime = timestamp;
490:
491: shmdate = datestamp;
492:
493: return 0;
494:
495: }
496:
497:
498:
499: static long
500:
501: shm_getname(root, dir, pathname)
502:
503: fcookie *root, *dir; char *pathname;
504:
505: {
506:
507: *pathname = 0;
508:
509: return 0;
510:
511: }
512:
513:
514:
515: static long
516:
517: shm_rename(olddir, oldname, newdir, newname)
518:
519: fcookie *olddir;
520:
521: char *oldname;
522:
523: fcookie *newdir;
524:
525: const char *newname;
526:
527: {
528:
529: SHMFILE *s;
530:
531:
532:
533: if (olddir->index != 0 || newdir->index != 0)
534:
535: return EPTHNF;
536:
537:
538:
539: /* verify that "newname" doesn't exist */
540:
541: for (s = shmroot; s; s = s->next)
542:
543: if (!stricmp(s->filename, newname))
544:
545: return EACCDN;
546:
547:
548:
549: for (s = shmroot; s; s = s->next)
550:
551: if (!stricmp(s->filename, oldname))
552:
553: break;
554:
555: if (!s)
556:
557: return EFILNF;
558:
559:
560:
561: strncpy(s->filename, newname, SHMNAME_MAX);
562:
563: shmtime = timestamp;
564:
565: shmdate = datestamp;
566:
567: return 0;
568:
569: }
570:
571:
572:
573: static long
574:
575: shm_opendir(dirh, flags)
576:
577: DIR *dirh;
578:
579: int flags;
580:
581: {
582:
583: dirh->index = 0;
584:
585: return 0;
586:
587: }
588:
589:
590:
591: static long
592:
593: shm_readdir(dirh, name, namelen, fc)
594:
595: DIR *dirh;
596:
597: char *name;
598:
599: int namelen;
600:
601: fcookie *fc;
602:
603: {
604:
605: int i;
606:
607: int giveindex = (dirh->flags == 0);
608:
609: SHMFILE *s;
610:
611:
612:
613: s = shmroot;
614:
615: i = dirh->index++;
616:
617: while (i > 0 && s != 0) {
618:
619: s = s->next;
620:
621: --i;
622:
623: }
624:
625: if (!s)
626:
627: return ENMFIL;
628:
629:
630:
631: fc->index = (long)s;
632:
633: fc->fs = &shm_filesys;
634:
635: fc->dev = SHMDEVICE;
636:
637:
638:
639: if (giveindex) {
640:
641: namelen -= sizeof(long);
642:
643: if (namelen <= 0) return ERANGE;
644:
645: *((long *)name) = (long)s;
646:
647: name += sizeof(long);
648:
649: }
650:
651: if (namelen < strlen(s->filename))
652:
653: return ENAMETOOLONG;
654:
655: strcpy(name, s->filename);
656:
657: return 0;
658:
659: }
660:
661:
662:
663: static long
664:
665: shm_rewinddir(dirh)
666:
667: DIR *dirh;
668:
669: {
670:
671: dirh->index = 0;
672:
673: return 0;
674:
675: }
676:
677:
678:
679: static long
680:
681: shm_closedir(dirh)
682:
683: DIR *dirh;
684:
685: {
686:
687: return 0;
688:
689: }
690:
691:
692:
693: static long
694:
695: shm_pathconf(dir, which)
696:
697: fcookie *dir;
698:
699: int which;
700:
701: {
702:
703: switch(which) {
704:
705: case -1:
706:
707: return DP_MAXREQ;
708:
709: case DP_IOPEN:
710:
711: return UNLIMITED; /* no internal limit on open files */
712:
713: case DP_MAXLINKS:
714:
715: return 1; /* we don't have hard links */
716:
717: case DP_PATHMAX:
718:
719: return PATH_MAX; /* max. path length */
720:
721: case DP_NAMEMAX:
722:
723: return SHMNAME_MAX; /* max. length of individual name */
724:
725: case DP_ATOMIC:
726:
727: return UNLIMITED; /* all writes are atomic */
728:
729: case DP_TRUNC:
730:
731: return DP_AUTOTRUNC; /* file names are truncated */
732:
733: case DP_CASE:
734:
735: return DP_CASEINSENS; /* case preserved, but ignored */
736:
737: default:
738:
739: return EINVFN;
740:
741: }
742:
743: }
744:
745:
746:
747: static long
748:
749: shm_dfree(dir, buf)
750:
751: fcookie *dir;
752:
753: long *buf;
754:
755: {
756:
757: long size;
758:
759: /* "sector" size is the size of the smallest amount of memory that can be
760:
761: allocated. see mem.h for the definition of ROUND
762:
763: */
764:
765: long secsiz = ROUND(1);
766:
767:
768:
769: size = tot_rsize(core, 0) + tot_rsize(alt, 0);
770:
771: *buf++ = size/secsiz; /* number of free clusters */
772:
773: size = tot_rsize(core, 1) + tot_rsize(alt, 1);
774:
775: *buf++ = size/secsiz; /* total number of clusters */
776:
777: *buf++ = secsiz; /* sector size (bytes) */
778:
779: *buf++ = 1; /* cluster size (in sectors) */
780:
781: return 0;
782:
783: }
784:
785:
786:
787: static DEVDRV *
788:
789: shm_getdev(fc, devsp)
790:
791: fcookie *fc;
792:
793: long *devsp;
794:
795: {
796:
797: SHMFILE *s;
798:
799:
800:
801: s = (SHMFILE *)fc->index;
802:
803:
804:
805: *devsp = (long)s;
806:
807: return &shm_device;
808:
809: }
810:
811:
812:
813: /*
814:
815: * create a shared memory region
816:
817: */
818:
819:
820:
821: static long
822:
823: shm_creat(dir, name, mode, attrib, fc)
824:
825: fcookie *dir;
826:
827: const char *name;
828:
829: unsigned mode;
830:
831: int attrib;
832:
833: fcookie *fc;
834:
835: {
836:
837: SHMFILE *s;
838:
839:
840:
841: /*
842:
843: * see if the name already exists
844:
845: */
846:
847: for (s = shmroot; s; s = s->next) {
848:
849: if (!stricmp(s->filename, name)) {
850:
851: DEBUG("shm_creat: file exists");
852:
853: return EACCDN;
854:
855: }
856:
857: }
858:
859:
860:
861: s = (SHMFILE *)kmalloc(SIZEOF(SHMFILE));
862:
863: if (!s)
864:
865: return ENSMEM;
866:
867:
868:
869: s->inuse = 0;
870:
871: strncpy(s->filename, name, SHMNAME_MAX);
872:
873: s->filename[SHMNAME_MAX] = 0;
874:
875: s->uid = curproc->ruid;
876:
877: s->gid = curproc->rgid;
878:
879: s->mode = mode;
880:
881: s->next = shmroot;
882:
883: s->reg = 0;
884:
885: s->time = shmtime = timestamp;
886:
887: s->date = shmdate = datestamp;
888:
889: shmroot = s;
890:
891:
892:
893: fc->fs = &shm_filesys;
894:
895: fc->index = (long)s;
896:
897: fc->dev = dir->dev;
898:
899:
900:
901: return 0;
902:
903: }
904:
905:
906:
907: /*
908:
909: * Shared memory device driver
910:
911: */
912:
913:
914:
915: /*
916:
917: * BUG: file locking and the O_SHMODE restrictions are not implemented
918:
919: * for shared memory
920:
921: */
922:
923:
924:
925: static long
926:
927: shm_open(f)
928:
929: FILEPTR *f;
930:
931: {
932:
933: SHMFILE *s;
934:
935:
936:
937: s = (SHMFILE *)f->devinfo;
938:
939: s->inuse++;
940:
941: return 0;
942:
943: }
944:
945:
946:
947: static long
948:
949: shm_write(f, buf, nbytes)
950:
951: FILEPTR *f; const char *buf; long nbytes;
952:
953: {
954:
955: SHMFILE *s;
956:
957: char *where;
958:
959: long bytes_written = 0;
960:
961:
962:
963: s = (SHMFILE *)f->devinfo;
964:
965: if (!s->reg)
966:
967: return 0;
968:
969:
970:
971: if (nbytes + f->pos > s->reg->len)
972:
973: nbytes = s->reg->len - f->pos;
974:
975:
976:
977: where = (char *)s->reg->loc + f->pos;
978:
979:
980:
981: /* BUG: memory read/writes should check for valid addresses */
982:
983:
984:
985: TRACE("shm_write: %ld bytes to %lx", nbytes, where);
986:
987:
988:
989: while (nbytes-- > 0) {
990:
991: *where++ = *buf++;
992:
993: bytes_written++;
994:
995: }
996:
997: f->pos += bytes_written;
998:
999: s->time = timestamp;
1000:
1001: s->date = datestamp;
1002:
1003: return bytes_written;
1004:
1005: }
1006:
1007:
1008:
1009: static long
1010:
1011: shm_read(f, buf, nbytes)
1012:
1013: FILEPTR *f; char *buf; long nbytes;
1014:
1015: {
1016:
1017: SHMFILE *s;
1018:
1019: char *where;
1020:
1021: long bytes_read = 0;
1022:
1023:
1024:
1025: s = (SHMFILE *)f->devinfo;
1026:
1027: if (!(s->reg))
1028:
1029: return 0;
1030:
1031:
1032:
1033: if (nbytes + f->pos > s->reg->len)
1034:
1035: nbytes = s->reg->len - f->pos;
1036:
1037:
1038:
1039: where = (char *)s->reg->loc + f->pos;
1040:
1041:
1042:
1043: TRACE("shm_read: %ld bytes from %lx", nbytes, where);
1044:
1045:
1046:
1047: while (nbytes-- > 0) {
1048:
1049: *buf++ = *where++;
1050:
1051: bytes_read++;
1052:
1053: }
1054:
1055: f->pos += bytes_read;
1056:
1057: return bytes_read;
1058:
1059: }
1060:
1061:
1062:
1063: /*
1064:
1065: * shm_ioctl: currently, the only IOCTL's available are:
1066:
1067: * SHMSETBLK: set the address of the shared memory file. This
1068:
1069: * call may only be made once per region, and then only
1070:
1071: * if the region is open for writing.
1072:
1073: * SHMGETBLK: get the address of the shared memory region. This
1074:
1075: * call fails (returns 0) if SHMSETBLK has not been
1076:
1077: * called yet for this shared memory file.
1078:
1079: */
1080:
1081:
1082:
1083: static long
1084:
1085: shm_ioctl(f, mode, buf)
1086:
1087: FILEPTR *f; int mode; void *buf;
1088:
1089: {
1090:
1091: SHMFILE *s;
1092:
1093: MEMREGION *m;
1094:
1095: int i;
1096:
1097: long r;
1098:
1099:
1100:
1101: s = (SHMFILE *)f->devinfo;
1102:
1103: switch(mode) {
1104:
1105: case SHMSETBLK:
1106:
1107: if (s->reg) {
1108:
1109: DEBUG("Fcntl: SHMSETBLK already performed for %s",
1110:
1111: s->filename);
1112:
1113: return ERANGE;
1114:
1115: }
1116:
1117: if ((f->flags & O_RWMODE) == O_RDONLY) {
1118:
1119: DEBUG("Fcntl: SHMSETBLK: %s was opened read-only",
1120:
1121: s->filename);
1122:
1123: return EACCDN;
1124:
1125: }
1126:
1127: /* find the memory region to be attached */
1128:
1129: m = 0;
1130:
1131: for (i = curproc->num_reg - 1; i >= 0; i--) {
1132:
1133: if (curproc->addr[i] == (virtaddr)buf) {
1134:
1135: m = curproc->mem[i];
1136:
1137: break;
1138:
1139: }
1140:
1141: }
1142:
1143: if (!m || !buf) {
1144:
1145: DEBUG("Fcntl: SHMSETBLK: bad address %lx", buf);
1146:
1147: return EIMBA;
1148:
1149: }
1150:
1151: m->links++;
1152:
1153: s->reg = m;
1154:
1155: return 0;
1156:
1157: case SHMGETBLK:
1158:
1159: if (!(m = s->reg)) {
1160:
1161: DEBUG("Fcntl: no address for SHMGETBLK");
1162:
1163: return 0;
1164:
1165: }
1166:
1167: /* check for memory limits */
1168:
1169: if (curproc->maxmem) {
1170:
1171: if (m->len > curproc->maxmem - memused(curproc)) {
1172:
1173: DEBUG("Fcntl: SHMGETBLK would violate memory limits");
1174:
1175: return 0;
1176:
1177: }
1178:
1179: }
1180:
1181: return (long)attach_region(curproc, m);
1182:
1183: case FIONREAD:
1184:
1185: case FIONWRITE:
1186:
1187: if (s->reg == 0) {
1188:
1189: r = 0;
1190:
1191: } else {
1192:
1193: r = s->reg->len - f->pos;
1194:
1195: if (r < 0) r = 0;
1196:
1197: }
1198:
1199: *((long *)buf) = r;
1200:
1201: return 0;
1202:
1203: default:
1204:
1205: DEBUG("shmfs: bad Fcntl command");
1206:
1207: }
1208:
1209: return EINVFN;
1210:
1211: }
1212:
1213:
1214:
1215: static long
1216:
1217: shm_lseek(f, where, whence)
1218:
1219: FILEPTR *f; long where; int whence;
1220:
1221: {
1222:
1223: long newpos = 0, maxpos;
1224:
1225: SHMFILE *s;
1226:
1227:
1228:
1229: s = (SHMFILE *)f->devinfo;
1230:
1231:
1232:
1233: if (s->reg)
1234:
1235: maxpos = s->reg->len;
1236:
1237: else
1238:
1239: maxpos = 0;
1240:
1241:
1242:
1243: switch(whence) {
1244:
1245: case 0:
1246:
1247: newpos = where;
1248:
1249: break;
1250:
1251: case 1:
1252:
1253: newpos = f->pos + where;
1254:
1255: break;
1256:
1257: case 2:
1258:
1259: newpos = maxpos - where;
1260:
1261: break;
1262:
1263: default:
1264:
1265: return EINVFN;
1266:
1267: }
1268:
1269:
1270:
1271: if (newpos < 0 || newpos > maxpos)
1272:
1273: return ERANGE;
1274:
1275:
1276:
1277: f->pos = newpos;
1278:
1279: return newpos;
1280:
1281: }
1282:
1283:
1284:
1285: static long
1286:
1287: shm_datime(f, timeptr, rwflag)
1288:
1289: FILEPTR *f;
1290:
1291: short *timeptr;
1292:
1293: int rwflag;
1294:
1295: {
1296:
1297: SHMFILE *s;
1298:
1299:
1300:
1301: s = (SHMFILE *)f->devinfo;
1302:
1303: if (rwflag) {
1304:
1305: s->time = *timeptr++;
1306:
1307: s->date = *timeptr;
1308:
1309: } else {
1310:
1311: *timeptr++ = s->time;
1312:
1313: *timeptr++ = s->date;
1314:
1315: }
1316:
1317: return 0;
1318:
1319: }
1320:
1321:
1322:
1323: static long
1324:
1325: shm_close(f, pid)
1326:
1327: FILEPTR *f;
1328:
1329: int pid;
1330:
1331: {
1332:
1333: SHMFILE *s;
1334:
1335:
1336:
1337: if (f->links <= 0) {
1338:
1339: s = (SHMFILE *)f->devinfo;
1340:
1341: s->inuse--;
1342:
1343: }
1344:
1345: return 0;
1346:
1347: }
1348:
1349:
1350:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.