Lines of
xvfs-core.c
from check-in 10f67b2ced
that are changed by the sequence of edits moving toward
check-in e786b9e07b:
1: #include <xvfs-core.h>
2: #include <string.h>
3: #include <sys/stat.h>
4: #include <errno.h>
5: #include <fcntl.h>
6: #include <tcl.h>
7:
8: #ifdef XVFS_DEBUG
9: #include <stdio.h> /* Needed for XVFS_DEBUG_PRINTF */
10: static int xvfs_debug_depth = 0;
11: #define XVFS_DEBUG_PRINTF(fmt, ...) fprintf(stderr, "[XVFS:DEBUG:%-30s:%4i] %s" fmt "\n", __func__, __LINE__, " " + (80 - (xvfs_debug_depth * 4)), __VA_ARGS__)
12: #define XVFS_DEBUG_PUTS(str) XVFS_DEBUG_PRINTF("%s", str);
13: #define XVFS_DEBUG_ENTER { xvfs_debug_depth++; XVFS_DEBUG_PUTS("Entered"); }
14: #define XVFS_DEBUG_LEAVE { XVFS_DEBUG_PUTS("Returning"); xvfs_debug_depth--; }
15: #else /* XVFS_DEBUG */
16: #define XVFS_DEBUG_PRINTF(fmt, ...) /**/
17: #define XVFS_DEBUG_PUTS(str) /**/
18: #define XVFS_DEBUG_ENTER /**/
19: #define XVFS_DEBUG_LEAVE /**/
20: #endif /* XVFS_DEBUG */
21:
22: #if defined(XVFS_MODE_FLEXIBLE) || defined(XVFS_MODE_SERVER)
23: #define XVFS_INTERNAL_SERVER_MAGIC "\xD4\xF3\x05\x96\x25\xCF\xAF\xFE"
24: #define XVFS_INTERNAL_SERVER_MAGIC_LEN 8
25:
26: struct xvfs_tclfs_server_info {
27: char magic[XVFS_INTERNAL_SERVER_MAGIC_LEN];
28: int (*registerProc)(Tcl_Interp *interp, struct Xvfs_FSInfo *fsInfo);
29: };
30: #endif /* XVFS_MODE_FLEXIBLE || XVFS_MODE_SERVER */
31:
32: #if defined(XVFS_MODE_SERVER) || defined(XVFS_MODE_STANDALONE) || defined(XVFS_MODE_FLEXIBLE)
33: #define XVFS_ROOT_MOUNTPOINT "//xvfs:/"
34:
35: struct xvfs_tclfs_instance_info {
36: struct Xvfs_FSInfo *fsInfo;
37: Tcl_Obj *mountpoint;
38: };
39:
40: /*
41: * Internal Core Utilities
42: */
43: static Tcl_Obj *xvfs_absolutePath(Tcl_Obj *path) {
44: Tcl_Obj *currentDirectory;
45: const char *pathStr;
46:
47: XVFS_DEBUG_ENTER;
48:
49: pathStr = Tcl_GetString(path);
50:
51: if (pathStr[0] != '/') {
52: currentDirectory = Tcl_FSGetCwd(NULL);
53: Tcl_IncrRefCount(currentDirectory);
54:
55: path = Tcl_ObjPrintf("%s/%s", Tcl_GetString(currentDirectory), pathStr);
56: Tcl_IncrRefCount(path);
57: Tcl_DecrRefCount(currentDirectory);
58: } else {
59: Tcl_IncrRefCount(path);
60: }
61:
62: XVFS_DEBUG_PRINTF("Converted path \"%s\" to absolute path: \"%s\"", pathStr, Tcl_GetString(path));
63:
64: XVFS_DEBUG_LEAVE;
65: return(path);
66: }
67:
68: static const char *xvfs_relativePath(Tcl_Obj *path, struct xvfs_tclfs_instance_info *info) {
69: const char *pathStr, *rootStr;
70: const char *pathFinal;
71: int pathLen, rootLen;
72:
73: XVFS_DEBUG_ENTER;
74:
75: rootStr = Tcl_GetStringFromObj(info->mountpoint, &rootLen);
76: pathStr = Tcl_GetStringFromObj(path, &pathLen);
77:
78: XVFS_DEBUG_PRINTF("Finding relative path of \"%s\" from \"%s\" ...", pathStr, rootStr);
79:
80: if (pathLen < rootLen) {
81: XVFS_DEBUG_PUTS("... none possible (length)");
82:
83: XVFS_DEBUG_LEAVE;
84: return(NULL);
85: }
86:
87: if (memcmp(pathStr, rootStr, rootLen) != 0) {
88: XVFS_DEBUG_PUTS("... none possible (prefix differs)");
89:
90: XVFS_DEBUG_LEAVE;
91: return(NULL);
92: }
93:
94: if (pathLen == rootLen) {
95: XVFS_DEBUG_PUTS("... short circuit: \"\"");
96:
97: XVFS_DEBUG_LEAVE;
98: return("");
99: }
100:
101: /* XXX:TODO: Should this use the native OS path separator ? */
102: if (pathStr[rootLen] != '/') {
103: XVFS_DEBUG_PUTS("... none possible (no seperator)");
104:
105: XVFS_DEBUG_LEAVE;
106: return(NULL);
107: }
108:
109: pathFinal = pathStr + rootLen + 1;
110: pathLen -= rootLen + 1;
111:
112: if (pathLen == 1 && memcmp(pathFinal, ".", 1) == 0) {
113: XVFS_DEBUG_PUTS("... short circuit: \".\" -> \"\"");
114:
115: XVFS_DEBUG_LEAVE;
116: return("");
117: }
118:
119: while (pathLen >= 2 && memcmp(pathFinal, "./", 2) == 0) {
120: pathFinal += 2;
121: pathLen -= 2;
122: }
123:
124: XVFS_DEBUG_PRINTF("... relative path: \"%s\"", pathFinal);
125:
126: XVFS_DEBUG_LEAVE;
127: return(pathFinal);
128: }
129:
130: static int xvfs_errorToErrno(int xvfs_error) {
131: if (xvfs_error >= 0) {
132: return(0);
133: }
134:
135: switch (xvfs_error) {
136: case XVFS_RV_ERR_ENOENT:
137: return(ENOENT);
138: case XVFS_RV_ERR_EINVAL:
139: return(EINVAL);
140: case XVFS_RV_ERR_EISDIR:
141: return(EISDIR);
142: case XVFS_RV_ERR_ENOTDIR:
143: return(ENOTDIR);
144: case XVFS_RV_ERR_EFAULT:
145: return(EFAULT);
146: case XVFS_RV_ERR_EROFS:
147: return(EROFS);
148: case XVFS_RV_ERR_INTERNAL:
149: return(EINVAL);
150: default:
151: return(ERANGE);
152: }
153: }
154:
155: static const char *xvfs_perror(int xvfs_error) {
156: if (xvfs_error >= 0) {
157: return("Not an error");
158: }
159:
160: switch (xvfs_error) {
161: case XVFS_RV_ERR_ENOENT:
162: case XVFS_RV_ERR_EINVAL:
163: case XVFS_RV_ERR_EISDIR:
164: case XVFS_RV_ERR_ENOTDIR:
165: case XVFS_RV_ERR_EFAULT:
166: case XVFS_RV_ERR_EROFS:
167: return(Tcl_ErrnoMsg(xvfs_errorToErrno(xvfs_error)));
168: case XVFS_RV_ERR_INTERNAL:
169: return("Internal error");
170: default:
171: return("Unknown error");
172: }
173: }
174:
175: /*
176: * Xvfs Memory Channel
177: */
178: struct xvfs_tclfs_channel_id {
179: Tcl_Channel channel;
180: struct xvfs_tclfs_instance_info *fsInstanceInfo;
181: Tcl_Obj *path;
182: Tcl_WideInt currentOffset;
183: Tcl_WideInt fileSize;
184: int eofMarked;
185: int queuedEvents;
186: int closed;
187: };
188: struct xvfs_tclfs_channel_event {
189: Tcl_Event tcl;
190: struct xvfs_tclfs_channel_id *channelInstanceData;
191: };
192: static Tcl_ChannelType xvfs_tclfs_channelType;
193:
10f67b2ced 2019-09-16 194: static Tcl_Channel xvfs_tclfs_openChannel(Tcl_Obj *path, struct xvfs_tclfs_instance_info *instanceInfo) {
195: struct xvfs_tclfs_channel_id *channelInstanceData;
196: Tcl_Channel channel;
197: Tcl_StatBuf fileInfo;
198: Tcl_Obj *channelName;
199: int statRet;
200:
201: XVFS_DEBUG_ENTER;
202: XVFS_DEBUG_PRINTF("Opening file \"%s\" ...", Tcl_GetString(path));
203:
204: statRet = instanceInfo->fsInfo->getStatProc(Tcl_GetString(path), &fileInfo);
205: if (statRet < 0) {
206: XVFS_DEBUG_PRINTF("... failed: %s", xvfs_perror(statRet));
207:
208: XVFS_DEBUG_LEAVE;
209: return(NULL);
210: }
211:
212: channelInstanceData = (struct xvfs_tclfs_channel_id *) Tcl_Alloc(sizeof(*channelInstanceData));
213: channelInstanceData->currentOffset = 0;
214: channelInstanceData->eofMarked = 0;
215: channelInstanceData->queuedEvents = 0;
216: channelInstanceData->closed = 0;
217: channelInstanceData->channel = NULL;
218:
219: channelName = Tcl_ObjPrintf("xvfs0x%llx", (unsigned long long) channelInstanceData);
220: if (!channelName) {
221: XVFS_DEBUG_PUTS("... failed");
222:
223: Tcl_Free((char *) channelInstanceData);
224:
225: XVFS_DEBUG_LEAVE;
226: return(NULL);
227: }
228: Tcl_IncrRefCount(channelName);
229:
230: channelInstanceData->fsInstanceInfo = instanceInfo;
231: channelInstanceData->fileSize = fileInfo.st_size;
232: channelInstanceData->path = path;
233: Tcl_IncrRefCount(path);
234:
235: channel = Tcl_CreateChannel(&xvfs_tclfs_channelType, Tcl_GetString(channelName), channelInstanceData, TCL_READABLE);
236: Tcl_DecrRefCount(channelName);
237: if (!channel) {
238: XVFS_DEBUG_PUTS("... failed");
239:
240: Tcl_DecrRefCount(path);
241: Tcl_Free((char *) channelInstanceData);
242:
243: XVFS_DEBUG_LEAVE;
244: return(NULL);
245: }
246:
247: channelInstanceData->channel = channel;
248:
249: XVFS_DEBUG_PRINTF("... ok (%p)", channelInstanceData);
250:
251: XVFS_DEBUG_LEAVE;
252: return(channel);
253: }
254:
255: static int xvfs_tclfs_closeChannel(ClientData channelInstanceData_p, Tcl_Interp *interp);
256: static int xvfs_tclfs_closeChannelEvent(Tcl_Event *event_p, int flags) {
257: struct xvfs_tclfs_channel_id *channelInstanceData;
258: struct xvfs_tclfs_channel_event *event;
259:
260: event = (struct xvfs_tclfs_channel_event *) event_p;
261: channelInstanceData = event->channelInstanceData;
262:
263: channelInstanceData->queuedEvents--;
264:
265: xvfs_tclfs_closeChannel((ClientData) channelInstanceData, NULL);
266:
267: return(1);
268: }
269:
270: static int xvfs_tclfs_closeChannel(ClientData channelInstanceData_p, Tcl_Interp *interp) {
271: struct xvfs_tclfs_channel_id *channelInstanceData;
272: struct xvfs_tclfs_channel_event *event;
273:
274: XVFS_DEBUG_ENTER;
275: XVFS_DEBUG_PRINTF("Closing channel %p ...", channelInstanceData_p);
276:
277: channelInstanceData = (struct xvfs_tclfs_channel_id *) channelInstanceData_p;
278:
279: channelInstanceData->closed = 1;
280:
281: if (channelInstanceData->queuedEvents != 0) {
282: XVFS_DEBUG_PUTS("... queued");
283:
284: event = (struct xvfs_tclfs_channel_event *) Tcl_Alloc(sizeof(*event));
285: event->tcl.proc = xvfs_tclfs_closeChannelEvent;
286: event->tcl.nextPtr = NULL;
287: event->channelInstanceData = channelInstanceData;
288:
289: channelInstanceData->queuedEvents++;
290:
291: Tcl_QueueEvent((Tcl_Event *) event, TCL_QUEUE_TAIL);
292:
293: XVFS_DEBUG_LEAVE;
294: return(0);
295: }
296:
297: Tcl_DecrRefCount(channelInstanceData->path);
298: Tcl_Free((char *) channelInstanceData);
299:
300: XVFS_DEBUG_PUTS("... ok");
301:
302: XVFS_DEBUG_LEAVE;
303: return(0);
304: }
305:
306: static int xvfs_tclfs_readChannel(ClientData channelInstanceData_p, char *buf, int bufSize, int *errorCodePtr) {
307: struct xvfs_tclfs_channel_id *channelInstanceData;
308: const unsigned char *data;
309: Tcl_WideInt offset, length;
310: char *path;
311:
312: channelInstanceData = (struct xvfs_tclfs_channel_id *) channelInstanceData_p;
313:
314: /*
315: * If we are already at the end of the file we can skip
316: * attempting to read it
317: */
318: if (channelInstanceData->eofMarked) {
319: return(0);
320: }
321:
322: path = Tcl_GetString(channelInstanceData->path);
323: offset = channelInstanceData->currentOffset;
324: length = bufSize;
325:
326: data = channelInstanceData->fsInstanceInfo->fsInfo->getDataProc(path, offset, &length);
327:
328: if (length < 0) {
329: *errorCodePtr = xvfs_errorToErrno(length);
330:
331: return(-1);
332: }
333:
334: if (length == 0) {
335: channelInstanceData->eofMarked = 1;
336: } else {
337: memcpy(buf, data, length);
338:
339: channelInstanceData->currentOffset += length;
340: }
341:
342: return(length);
343: }
344:
345: static int xvfs_tclfs_watchChannelEvent(Tcl_Event *event_p, int flags) {
346: struct xvfs_tclfs_channel_id *channelInstanceData;
347: struct xvfs_tclfs_channel_event *event;
348:
349: event = (struct xvfs_tclfs_channel_event *) event_p;
350: channelInstanceData = event->channelInstanceData;
351:
352: channelInstanceData->queuedEvents--;
353:
354: if (channelInstanceData->closed) {
355: return(1);
356: }
357:
358: Tcl_NotifyChannel(channelInstanceData->channel, TCL_READABLE);
359:
360: return(1);
361: }
362:
363: static void xvfs_tclfs_watchChannel(ClientData channelInstanceData_p, int mask) {
364: struct xvfs_tclfs_channel_id *channelInstanceData;
365: struct xvfs_tclfs_channel_event *event;
366:
367: if ((mask & TCL_READABLE) != TCL_READABLE) {
368: return;
369: }
370:
371: channelInstanceData = (struct xvfs_tclfs_channel_id *) channelInstanceData_p;
372:
373: /*
374: * If the read call has marked that we have reached EOF,
375: * do not signal any further
376: */
377: if (channelInstanceData->eofMarked) {
378: return;
379: }
380:
381: event = (struct xvfs_tclfs_channel_event *) Tcl_Alloc(sizeof(*event));
382: event->tcl.proc = xvfs_tclfs_watchChannelEvent;
383: event->tcl.nextPtr = NULL;
384: event->channelInstanceData = channelInstanceData;
385:
386: channelInstanceData->queuedEvents++;
387:
388: Tcl_QueueEvent((Tcl_Event *) event, TCL_QUEUE_TAIL);
389:
390: return;
391: }
392:
393: static int xvfs_tclfs_seekChannel(ClientData channelInstanceData_p, long offset, int mode, int *errorCodePtr) {
394: struct xvfs_tclfs_channel_id *channelInstanceData;
395: Tcl_WideInt newOffset, fileSize;
396:
397: channelInstanceData = (struct xvfs_tclfs_channel_id *) channelInstanceData_p;
398:
399: newOffset = channelInstanceData->currentOffset;
400: fileSize = channelInstanceData->fileSize;
401:
402: switch (mode) {
403: case SEEK_CUR:
404: newOffset += offset;
405: break;
406: case SEEK_SET:
407: newOffset = offset;
408: break;
409: case SEEK_END:
410: newOffset = fileSize + offset;
411: break;
412: default:
413: *errorCodePtr = xvfs_errorToErrno(XVFS_RV_ERR_EINVAL);
414:
415: return(-1);
416: }
417:
418: /*
419: * We allow users to seek right up to the end of the buffer, but
420: * no further, this way if they want to seek backwards from there
421: * it is possible to do so.
422: */
423: if (newOffset < 0 || newOffset > fileSize) {
424: *errorCodePtr = xvfs_errorToErrno(XVFS_RV_ERR_EINVAL);
425:
426: return(-1);
427: }
428:
429: if (newOffset != channelInstanceData->currentOffset) {
430: channelInstanceData->eofMarked = 0;
431: channelInstanceData->currentOffset = newOffset;
432: }
433:
434: return(channelInstanceData->currentOffset);
435: }
436:
437: static void xvfs_tclfs_prepareChannelType(void) {
438: xvfs_tclfs_channelType.typeName = "xvfs";
439: xvfs_tclfs_channelType.version = TCL_CHANNEL_VERSION_2;
440: xvfs_tclfs_channelType.closeProc = xvfs_tclfs_closeChannel;
441: xvfs_tclfs_channelType.inputProc = xvfs_tclfs_readChannel;
442: xvfs_tclfs_channelType.outputProc = NULL;
443: xvfs_tclfs_channelType.watchProc = xvfs_tclfs_watchChannel;
444: xvfs_tclfs_channelType.getHandleProc = NULL;
445: xvfs_tclfs_channelType.seekProc = xvfs_tclfs_seekChannel;
446: xvfs_tclfs_channelType.setOptionProc = NULL;
447: xvfs_tclfs_channelType.getOptionProc = NULL;
448: xvfs_tclfs_channelType.close2Proc = NULL;
449: xvfs_tclfs_channelType.blockModeProc = NULL;
450: xvfs_tclfs_channelType.flushProc = NULL;
451: xvfs_tclfs_channelType.handlerProc = NULL;
452: xvfs_tclfs_channelType.wideSeekProc = NULL;
453: xvfs_tclfs_channelType.threadActionProc = NULL;
454: xvfs_tclfs_channelType.truncateProc = NULL;
455: }
456:
457: /*
458: * Internal Tcl_Filesystem functions, with the appropriate instance info
459: */
460: static int xvfs_tclfs_pathInFilesystem(Tcl_Obj *path, ClientData *dataPtr, struct xvfs_tclfs_instance_info *instanceInfo) {
461: const char *relativePath;
462: int retval;
463:
464: XVFS_DEBUG_ENTER;
465:
466: XVFS_DEBUG_PRINTF("Checking to see if path \"%s\" is in the filesystem ...", Tcl_GetString(path));
467:
468: path = xvfs_absolutePath(path);
469:
470: relativePath = xvfs_relativePath(path, instanceInfo);
471:
472: retval = TCL_OK;
473: if (!relativePath) {
474: retval = -1;
475: }
476:
477: Tcl_DecrRefCount(path);
478:
479: XVFS_DEBUG_PRINTF("... %s", retval == -1 ? "no" : "yes");
480:
481: XVFS_DEBUG_LEAVE;
482: return(retval);
483: }
484:
485: static int xvfs_tclfs_stat(Tcl_Obj *path, Tcl_StatBuf *statBuf, struct xvfs_tclfs_instance_info *instanceInfo) {
486: const char *pathStr;
487: int retval;
488:
489: XVFS_DEBUG_ENTER;
490:
491: XVFS_DEBUG_PRINTF("Getting stat() on \"%s\" ...", Tcl_GetString(path));
492:
493: path = xvfs_absolutePath(path);
494:
495: pathStr = xvfs_relativePath(path, instanceInfo);
496:
497: retval = instanceInfo->fsInfo->getStatProc(pathStr, statBuf);
498: if (retval < 0) {
499: XVFS_DEBUG_PRINTF("... failed: %s", xvfs_perror(retval));
500: retval = -1;
501: } else {
502: XVFS_DEBUG_PUTS("... ok");
503: }
504:
505: Tcl_DecrRefCount(path);
506:
507: XVFS_DEBUG_LEAVE;
508: return(retval);
509: }
510:
511: static int xvfs_tclfs_access(Tcl_Obj *path, int mode, struct xvfs_tclfs_instance_info *instanceInfo) {
512: const char *pathStr;
513: Tcl_StatBuf fileInfo;
514: int statRetVal;
515:
516: XVFS_DEBUG_ENTER;
517:
518: XVFS_DEBUG_PRINTF("Getting access(..., %i) on \"%s\" ...", mode, Tcl_GetString(path));
519:
520: if (mode & W_OK) {
521: XVFS_DEBUG_PUTS("... no (not writable)");
522:
523: XVFS_DEBUG_LEAVE;
524: return(-1);
525: }
526:
527: path = xvfs_absolutePath(path);
528:
529: pathStr = xvfs_relativePath(path, instanceInfo);
530: if (!pathStr) {
531: XVFS_DEBUG_PUTS("... no (not in our path)");
532:
533: Tcl_DecrRefCount(path);
534:
535: XVFS_DEBUG_LEAVE;
536: return(-1);
537: }
538:
539: statRetVal = instanceInfo->fsInfo->getStatProc(pathStr, &fileInfo);
540: if (statRetVal < 0) {
541: XVFS_DEBUG_PUTS("... no (not statable)");
542:
543: Tcl_DecrRefCount(path);
544:
545: XVFS_DEBUG_LEAVE;
546: return(-1);
547: }
548:
549: if (mode & X_OK) {
550: if (!(fileInfo.st_mode & 040000)) {
551: XVFS_DEBUG_PUTS("... no (not a directory and X_OK specified)");
552:
553: Tcl_DecrRefCount(path);
554:
555: XVFS_DEBUG_LEAVE;
556: return(-1);
557: }
558: }
559:
560: Tcl_DecrRefCount(path);
561:
562: XVFS_DEBUG_PUTS("... ok");
563:
564: XVFS_DEBUG_LEAVE;
565: return(0);
566: }
567:
568: static Tcl_Channel xvfs_tclfs_openFileChannel(Tcl_Interp *interp, Tcl_Obj *path, int mode, int permissions, struct xvfs_tclfs_instance_info *instanceInfo) {
569: Tcl_Channel retval;
570: Tcl_Obj *pathRel;
571: const char *pathStr;
572:
573: XVFS_DEBUG_ENTER;
574:
575: XVFS_DEBUG_PRINTF("Asked to open(\"%s\", %x)...", Tcl_GetString(path), mode);
576:
577: if (mode & O_WRONLY) {
578: XVFS_DEBUG_PUTS("... failed (asked to open for writing)");
579:
10f67b2ced 2019-09-16 580: if (interp) {
10f67b2ced 2019-09-16 581: Tcl_SetErrno(xvfs_errorToErrno(XVFS_RV_ERR_EROFS));
10f67b2ced 2019-09-16 582: Tcl_SetResult(interp, (char *) Tcl_PosixError(interp), NULL);
10f67b2ced 2019-09-16 583: }
584:
585: XVFS_DEBUG_LEAVE;
586: return(NULL);
587: }
588:
589: path = xvfs_absolutePath(path);
590:
591: pathStr = xvfs_relativePath(path, instanceInfo);
592:
593: pathRel = Tcl_NewStringObj(pathStr, -1);
594:
595: Tcl_DecrRefCount(path);
596:
597: XVFS_DEBUG_PUTS("... done, passing off to channel handler");
598:
10f67b2ced 2019-09-16 599: retval = xvfs_tclfs_openChannel(pathRel, instanceInfo);
600:
601: XVFS_DEBUG_LEAVE;
602: return(retval);
603: }
604:
605: static int xvfs_tclfs_verifyType(Tcl_Obj *path, Tcl_GlobTypeData *types, struct xvfs_tclfs_instance_info *instanceInfo) {
606: const char *pathStr;
607: Tcl_StatBuf fileInfo;
608: int statRetVal;
609:
610: XVFS_DEBUG_ENTER;
611:
612: if (types) {
613: XVFS_DEBUG_PRINTF("Asked to verify the existence and type of \"%s\" matches type=%i and perm=%i ...", Tcl_GetString(path), types->type, types->perm);
614: } else {
615: XVFS_DEBUG_PRINTF("Asked to verify the existence \"%s\" ...", Tcl_GetString(path));
616: }
617:
618: statRetVal = xvfs_tclfs_stat(path, &fileInfo, instanceInfo);
619: if (statRetVal != 0) {
620: XVFS_DEBUG_PUTS("... no (cannot stat)");
621:
622: XVFS_DEBUG_LEAVE;
623: return(0);
624: }
625:
626: if (!types) {
627: XVFS_DEBUG_PUTS("... yes");
628:
629: XVFS_DEBUG_LEAVE;
630: return(1);
631: }
632:
633: if (types->perm != TCL_GLOB_PERM_RONLY) {
634: if (types->perm & (TCL_GLOB_PERM_W | TCL_GLOB_PERM_HIDDEN)) {
635: XVFS_DEBUG_PUTS("... no (checked for writable or hidden, not supported)");
636:
637: XVFS_DEBUG_LEAVE;
638: return(0);
639: }
640:
641: if ((types->perm & TCL_GLOB_PERM_X) == TCL_GLOB_PERM_X) {
642: if (!(fileInfo.st_mode & 040000)) {
643: XVFS_DEBUG_PUTS("... no (checked for executable but not a directory)");
644:
645: XVFS_DEBUG_LEAVE;
646: return(0);
647: }
648: }
649: }
650:
651: if (types->type & (TCL_GLOB_TYPE_BLOCK | TCL_GLOB_TYPE_CHAR | TCL_GLOB_TYPE_PIPE | TCL_GLOB_TYPE_SOCK | TCL_GLOB_TYPE_LINK)) {
652: XVFS_DEBUG_PUTS("... no (checked for block, char, pipe, sock, or link, not supported)");
653:
654: XVFS_DEBUG_LEAVE;
655: return(0);
656: }
657:
658: if ((types->type & TCL_GLOB_TYPE_DIR) == TCL_GLOB_TYPE_DIR) {
659: if (!(fileInfo.st_mode & 040000)) {
660: XVFS_DEBUG_PUTS("... no (checked for directory but not a directory)");
661:
662: XVFS_DEBUG_LEAVE;
663: return(0);
664: }
665: }
666:
667: if ((types->type & TCL_GLOB_TYPE_FILE) == TCL_GLOB_TYPE_FILE) {
668: if (!(fileInfo.st_mode & 0100000)) {
669: XVFS_DEBUG_PUTS("... no (checked for file but not a file)");
670:
671: XVFS_DEBUG_LEAVE;
672: return(0);
673: }
674: }
675:
676: if ((types->type & TCL_GLOB_TYPE_MOUNT) == TCL_GLOB_TYPE_MOUNT) {
677: path = xvfs_absolutePath(path);
678: pathStr = xvfs_relativePath(path, instanceInfo);
679: if (!pathStr) {
680: XVFS_DEBUG_PUTS("... no (checked for mount but not able to resolve path)");
681:
682: Tcl_DecrRefCount(path);
683:
684: XVFS_DEBUG_LEAVE;
685: return(0);
686: }
687:
688: if (strlen(pathStr) != 0) {
689: XVFS_DEBUG_PUTS("... no (checked for mount but not our top-level directory)");
690:
691: Tcl_DecrRefCount(path);
692:
693: XVFS_DEBUG_LEAVE;
694: return(0);
695: }
696:
697: Tcl_DecrRefCount(path);
698: }
699:
700: XVFS_DEBUG_PUTS("... yes");
701:
702: XVFS_DEBUG_LEAVE;
703: return(1);
704: }
705:
706: static int xvfs_tclfs_matchInDir(Tcl_Interp *interp, Tcl_Obj *resultPtr, Tcl_Obj *path, const char *pattern, Tcl_GlobTypeData *types, struct xvfs_tclfs_instance_info *instanceInfo) {
707: const char *pathStr;
708: const char **children, *child;
709: Tcl_WideInt childrenCount, idx;
710: Tcl_Obj *childObj;
711: int tclRetVal;
712:
713: if (pattern == NULL) {
714: if (xvfs_tclfs_verifyType(path, types, instanceInfo)) {
715: return(TCL_OK);
716: }
717:
718: return(TCL_ERROR);
719: }
720:
721: XVFS_DEBUG_ENTER;
722:
723: path = xvfs_absolutePath(path);
724:
725: if (types) {
726: XVFS_DEBUG_PRINTF("Checking for files matching %s in \"%s\" and type=%i and perm=%i ...", pattern, Tcl_GetString(path), types->type, types->perm);
727: } else {
728: XVFS_DEBUG_PRINTF("Checking for files matching %s in \"%s\" ...", pattern, Tcl_GetString(path));
729: }
730:
731: pathStr = xvfs_relativePath(path, instanceInfo);
732: if (!pathStr) {
733: XVFS_DEBUG_PUTS("... error (not in our VFS)");
734:
735: Tcl_DecrRefCount(path);
736:
10f67b2ced 2019-09-16 737: if (interp) {
10f67b2ced 2019-09-16 738: Tcl_SetResult(interp, (char *) xvfs_perror(XVFS_RV_ERR_ENOENT), NULL);
10f67b2ced 2019-09-16 739: }
740:
741: XVFS_DEBUG_LEAVE;
742: return(TCL_OK);
743: }
744:
745: childrenCount = 0;
746: children = instanceInfo->fsInfo->getChildrenProc(pathStr, &childrenCount);
747: if (childrenCount < 0) {
748: XVFS_DEBUG_PRINTF("... error: %s", xvfs_perror(childrenCount));
749:
750: Tcl_DecrRefCount(path);
751:
10f67b2ced 2019-09-16 752: if (interp) {
10f67b2ced 2019-09-16 753: Tcl_SetResult(interp, (char *) xvfs_perror(childrenCount), NULL);
10f67b2ced 2019-09-16 754: }
755:
756: XVFS_DEBUG_LEAVE;
757: return(TCL_ERROR);
758: }
759:
760: for (idx = 0; idx < childrenCount; idx++) {
761: child = children[idx];
762:
763: if (!Tcl_StringMatch(child, pattern)) {
764: continue;
765: }
766:
767: childObj = Tcl_DuplicateObj(path);
768: Tcl_IncrRefCount(childObj);
769: Tcl_AppendStringsToObj(childObj, "/", child, NULL);
770:
771: if (!xvfs_tclfs_verifyType(childObj, types, instanceInfo)) {
772: Tcl_DecrRefCount(childObj);
773:
774: continue;
775: }
776:
777: tclRetVal = Tcl_ListObjAppendElement(interp, resultPtr, childObj);
778: Tcl_DecrRefCount(childObj);
779:
780: if (tclRetVal != TCL_OK) {
781: XVFS_DEBUG_PUTS("... error (lappend)");
782: Tcl_DecrRefCount(path);
783:
784: XVFS_DEBUG_LEAVE;
785: return(tclRetVal);
786: }
787: }
788:
789: Tcl_DecrRefCount(path);
790:
791: XVFS_DEBUG_PRINTF("... ok (returning items: %s)", Tcl_GetString(resultPtr));
792:
793: XVFS_DEBUG_LEAVE;
794: return(TCL_OK);
795: }
796: #endif /* XVFS_MODE_SERVER || XVFS_MODE_STANDALONE || XVFS_MODE_FLEIXBLE */
797:
798: #if defined(XVFS_MODE_STANDALONE) || defined(XVFS_MODE_FLEXIBLE)
799: /*
800: * Tcl_Filesystem handlers for the standalone implementation
801: */
802: static struct xvfs_tclfs_instance_info xvfs_tclfs_standalone_info;
803: static int xvfs_tclfs_standalone_pathInFilesystem(Tcl_Obj *path, ClientData *dataPtr) {
804: return(xvfs_tclfs_pathInFilesystem(path, dataPtr, &xvfs_tclfs_standalone_info));
805: }
806:
807: static int xvfs_tclfs_standalone_stat(Tcl_Obj *path, Tcl_StatBuf *statBuf) {
808: return(xvfs_tclfs_stat(path, statBuf, &xvfs_tclfs_standalone_info));
809: }
810:
811: static int xvfs_tclfs_standalone_access(Tcl_Obj *path, int mode) {
812: return(xvfs_tclfs_access(path, mode, &xvfs_tclfs_standalone_info));
813: }
814:
815: static Tcl_Channel xvfs_tclfs_standalone_openFileChannel(Tcl_Interp *interp, Tcl_Obj *path, int mode, int permissions) {
816: return(xvfs_tclfs_openFileChannel(interp, path, mode, permissions, &xvfs_tclfs_standalone_info));
817: }
818:
819: static int xvfs_tclfs_standalone_matchInDir(Tcl_Interp *interp, Tcl_Obj *resultPtr, Tcl_Obj *pathPtr, const char *pattern, Tcl_GlobTypeData *types) {
820: return(xvfs_tclfs_matchInDir(interp, resultPtr, pathPtr, pattern, types, &xvfs_tclfs_standalone_info));
821: }
822:
823: /*
824: * There are three (3) modes of operation for Xvfs_Register:
825: * 1. standalone -- We register our own Tcl_Filesystem
826: * and handle requests under `//xvfs:/<fsName>`
827: * 2. client -- A single Tcl_Filesystem is registered for the
828: * interp to handle requests under `//xvfs:/` which
829: * then dispatches to the appropriate registered
830: * handler
831: * 3. flexible -- Attempts to find a core Xvfs instance for the
832: * process at runtime, if found do #2, otherwise
833: * fallback to #1
834: *
835: */
836: static Tcl_Filesystem xvfs_tclfs_standalone_fs;
837: static int xvfs_standalone_register(Tcl_Interp *interp, struct Xvfs_FSInfo *fsInfo) {
838: int tcl_ret;
839: static int registered = 0;
840:
841: /*
842: * Ensure this instance is not already registered
843: */
844: if (registered) {
845: return(TCL_OK);
846: }
847: registered = 1;
848:
849: /*
850: * In standalone mode, we only support the same protocol we are
851: * compiling for.
852: */
853: if (fsInfo->protocolVersion != XVFS_PROTOCOL_VERSION) {
854: if (interp) {
855: Tcl_SetResult(interp, "Protocol mismatch", NULL);
856: }
857: return(TCL_ERROR);
858: }
859:
860: xvfs_tclfs_standalone_fs.typeName = "xvfs";
861: xvfs_tclfs_standalone_fs.structureLength = sizeof(xvfs_tclfs_standalone_fs);
862: xvfs_tclfs_standalone_fs.version = TCL_FILESYSTEM_VERSION_1;
863: xvfs_tclfs_standalone_fs.pathInFilesystemProc = xvfs_tclfs_standalone_pathInFilesystem;
864: xvfs_tclfs_standalone_fs.dupInternalRepProc = NULL;
865: xvfs_tclfs_standalone_fs.freeInternalRepProc = NULL;
866: xvfs_tclfs_standalone_fs.internalToNormalizedProc = NULL;
867: xvfs_tclfs_standalone_fs.createInternalRepProc = NULL;
868: xvfs_tclfs_standalone_fs.normalizePathProc = NULL;
869: xvfs_tclfs_standalone_fs.filesystemPathTypeProc = NULL;
870: xvfs_tclfs_standalone_fs.filesystemSeparatorProc = NULL;
871: xvfs_tclfs_standalone_fs.statProc = xvfs_tclfs_standalone_stat;
872: xvfs_tclfs_standalone_fs.accessProc = xvfs_tclfs_standalone_access;
873: xvfs_tclfs_standalone_fs.openFileChannelProc = xvfs_tclfs_standalone_openFileChannel;
874: xvfs_tclfs_standalone_fs.matchInDirectoryProc = xvfs_tclfs_standalone_matchInDir;
875: xvfs_tclfs_standalone_fs.utimeProc = NULL;
876: xvfs_tclfs_standalone_fs.linkProc = NULL;
877: xvfs_tclfs_standalone_fs.listVolumesProc = NULL;
878: xvfs_tclfs_standalone_fs.fileAttrStringsProc = NULL;
879: xvfs_tclfs_standalone_fs.fileAttrsGetProc = NULL;
880: xvfs_tclfs_standalone_fs.fileAttrsSetProc = NULL;
881: xvfs_tclfs_standalone_fs.createDirectoryProc = NULL;
882: xvfs_tclfs_standalone_fs.removeDirectoryProc = NULL;
883: xvfs_tclfs_standalone_fs.deleteFileProc = NULL;
884: xvfs_tclfs_standalone_fs.copyFileProc = NULL;
885: xvfs_tclfs_standalone_fs.renameFileProc = NULL;
886: xvfs_tclfs_standalone_fs.copyDirectoryProc = NULL;
887: xvfs_tclfs_standalone_fs.lstatProc = NULL;
888: xvfs_tclfs_standalone_fs.loadFileProc = NULL;
889: xvfs_tclfs_standalone_fs.getCwdProc = NULL;
890: xvfs_tclfs_standalone_fs.chdirProc = NULL;
891:
892: xvfs_tclfs_standalone_info.fsInfo = fsInfo;
893: xvfs_tclfs_standalone_info.mountpoint = Tcl_NewObj();
894:
895: Tcl_IncrRefCount(xvfs_tclfs_standalone_info.mountpoint);
896: Tcl_AppendStringsToObj(xvfs_tclfs_standalone_info.mountpoint, XVFS_ROOT_MOUNTPOINT, fsInfo->name, NULL);
897:
898: tcl_ret = Tcl_FSRegister(NULL, &xvfs_tclfs_standalone_fs);
899: if (tcl_ret != TCL_OK) {
900: Tcl_DecrRefCount(xvfs_tclfs_standalone_info.mountpoint);
901:
902: if (interp) {
903: Tcl_SetResult(interp, "Tcl_FSRegister() failed", NULL);
904: }
905:
906: return(tcl_ret);
907: }
908:
909: xvfs_tclfs_prepareChannelType();
910:
911: return(TCL_OK);
912: }
913: #endif /* XVFS_MODE_STANDALONE || XVFS_MODE_FLEXIBLE */
914:
915: #if defined(XVFS_MODE_FLEXIBLE)
916: static int xvfs_flexible_register(Tcl_Interp *interp, struct Xvfs_FSInfo *fsInfo) {
917: ClientData fsHandlerDataRaw;
918: struct xvfs_tclfs_server_info *fsHandlerData;
919: const Tcl_Filesystem *fsHandler;
920: int (*xvfs_register)(Tcl_Interp *interp, struct Xvfs_FSInfo *fsInfo);
921: Tcl_Obj *rootPathObj;
922:
923: xvfs_register = &xvfs_standalone_register;
924:
925: rootPathObj = Tcl_NewStringObj(XVFS_ROOT_MOUNTPOINT, -1);
926: if (!rootPathObj) {
927: return(xvfs_register(interp, fsInfo));
928: }
929:
930: Tcl_IncrRefCount(rootPathObj);
931: fsHandler = Tcl_FSGetFileSystemForPath(rootPathObj);
932: Tcl_DecrRefCount(rootPathObj);
933:
934: if (!fsHandler) {
935: return(xvfs_register(interp, fsInfo));
936: }
937:
938: fsHandlerDataRaw = Tcl_FSData(fsHandler);
939: if (!fsHandlerDataRaw) {
940: return(xvfs_register(interp, fsInfo));
941: }
942:
943: fsHandlerData = (struct xvfs_tclfs_server_info *) fsHandlerDataRaw;
944:
945: /*
946: * XXX:TODO: What is the chance that the handler for //xvfs:/ hold
947: * client data smaller than XVFS_INTERNAL_SERVER_MAGIC_LEN ?
948: */
949: if (memcmp(fsHandlerData->magic, XVFS_INTERNAL_SERVER_MAGIC, sizeof(fsHandlerData->magic)) == 0) {
950: xvfs_register = fsHandlerData->registerProc;
951: }
952:
953: return(xvfs_register(interp, fsInfo));
954: }
955: #endif /* XVFS_MODE_FLEXIBLE */
956:
957: #if defined(XVFS_MODE_SERVER)
958: int Xvfs_Register(Tcl_Interp *interp, struct Xvfs_FSInfo *fsInfo) {
959: return(TCL_ERROR);
960: }
961: #endif /* XVFS_MODE_SERVER */