Lines of
xvfs-core.c
from check-in aa08a4a749
that are changed by the sequence of edits moving toward
check-in 5583d77f1c:
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: #if defined(XVFS_MODE_FLEXIBLE) || defined(XVFS_MODE_SERVER)
9: #define XVFS_INTERNAL_SERVER_MAGIC "\xD4\xF3\x05\x96\x25\xCF\xAF\xFE"
10: #define XVFS_INTERNAL_SERVER_MAGIC_LEN 8
11:
12: struct xvfs_tclfs_server_info {
13: char magic[XVFS_INTERNAL_SERVER_MAGIC_LEN];
14: int (*registerProc)(Tcl_Interp *interp, struct Xvfs_FSInfo *fsInfo);
15: };
16: #endif /* XVFS_MODE_FLEXIBLE || XVFS_MODE_SERVER */
17:
18: #if defined(XVFS_MODE_SERVER) || defined(XVFS_MODE_STANDALONE) || defined(XVFS_MODE_FLEXIBLE)
19: #define XVFS_ROOT_MOUNTPOINT "//xvfs:/"
20:
21: struct xvfs_tclfs_instance_info {
22: struct Xvfs_FSInfo *fsInfo;
23: Tcl_Obj *mountpoint;
24: };
25:
26: /*
27: * Internal Core Utilities
28: */
29: static const char *xvfs_relativePath(Tcl_Obj *path, struct xvfs_tclfs_instance_info *info) {
30: const char *pathStr, *rootStr;
31: int pathLen, rootLen;
32:
33: pathStr = Tcl_GetStringFromObj(path, &pathLen);
34: rootStr = Tcl_GetStringFromObj(info->mountpoint, &rootLen);
35:
36: if (pathLen < rootLen) {
37: return(NULL);
38: }
39:
40: if (memcmp(pathStr, rootStr, rootLen) != 0) {
41: return(NULL);
42: }
43:
44: if (pathLen == rootLen) {
45: return("");
46: }
47:
48: /* XXX:TODO: Should this use the native OS path separator ? */
49: if (pathStr[rootLen] != '/') {
50: return(NULL);
51: }
52:
aa08a4a749 2019-09-14 53: return(pathStr + rootLen + 1);
aa08a4a749 2019-09-14 54: }
aa08a4a749 2019-09-14 55:
aa08a4a749 2019-09-14 56: #if 0
aa08a4a749 2019-09-14 57: /*
aa08a4a749 2019-09-14 58: * Currently unused
aa08a4a749 2019-09-14 59: */
60: static const char *xvfs_perror(int xvfs_error) {
61: if (xvfs_error >= 0) {
62: return("Not an error");
63: }
64:
65: switch (xvfs_error) {
66: case XVFS_RV_ERR_ENOENT:
67: return("No such file or directory");
68: case XVFS_RV_ERR_EINVAL:
69: return("Invalid argument");
70: case XVFS_RV_ERR_EISDIR:
71: return("Is a directory");
72: case XVFS_RV_ERR_ENOTDIR:
73: return("Not a directory");
74: case XVFS_RV_ERR_EFAULT:
75: return("Bad address");
76: default:
77: return("Unknown error");
78: }
79: }
aa08a4a749 2019-09-14 80: #endif
81:
82: static int xvfs_errorToErrno(int xvfs_error) {
83: if (xvfs_error >= 0) {
84: return(0);
85: }
86:
87: switch (xvfs_error) {
88: case XVFS_RV_ERR_ENOENT:
89: return(ENOENT);
90: case XVFS_RV_ERR_EINVAL:
91: return(EINVAL);
92: case XVFS_RV_ERR_EISDIR:
93: return(EISDIR);
94: case XVFS_RV_ERR_ENOTDIR:
95: return(ENOTDIR);
96: case XVFS_RV_ERR_EFAULT:
97: return(EFAULT);
98: default:
99: return(ERANGE);
100: }
101: }
102:
103: /*
104: * Xvfs Memory Channel
105: */
106: struct xvfs_tclfs_channel_id {
107: Tcl_Channel channel;
108: struct xvfs_tclfs_instance_info *fsInstanceInfo;
109: Tcl_Obj *path;
110: Tcl_WideInt currentOffset;
111: Tcl_WideInt fileSize;
112: int eofMarked;
113: int queuedEvents;
114: int closed;
115: };
116: struct xvfs_tclfs_channel_event {
117: Tcl_Event tcl;
118: struct xvfs_tclfs_channel_id *channelInstanceData;
119: };
120: static Tcl_ChannelType xvfs_tclfs_channelType;
121:
122: static Tcl_Channel xvfs_tclfs_openChannel(Tcl_Obj *path, struct xvfs_tclfs_instance_info *instanceInfo) {
123: struct xvfs_tclfs_channel_id *channelInstanceData;
124: Tcl_Channel channel;
125: Tcl_StatBuf fileInfo;
126: Tcl_Obj *channelName;
127: int statRet;
128:
129: statRet = instanceInfo->fsInfo->getStatProc(Tcl_GetString(path), &fileInfo);
130: if (statRet < 0) {
131: return(NULL);
132: }
133:
134: channelInstanceData = (struct xvfs_tclfs_channel_id *) Tcl_Alloc(sizeof(*channelInstanceData));
135: channelInstanceData->currentOffset = 0;
136: channelInstanceData->eofMarked = 0;
137: channelInstanceData->queuedEvents = 0;
138: channelInstanceData->closed = 0;
139: channelInstanceData->channel = NULL;
140:
141: channelName = Tcl_ObjPrintf("xvfs0x%llx", (unsigned long long) channelInstanceData);
142: if (!channelName) {
143: Tcl_Free((char *) channelInstanceData);
144:
145: return(NULL);
146: }
147: Tcl_IncrRefCount(channelName);
148:
149: channelInstanceData->fsInstanceInfo = instanceInfo;
150: channelInstanceData->fileSize = fileInfo.st_size;
151: channelInstanceData->path = path;
152: Tcl_IncrRefCount(path);
153:
154: channel = Tcl_CreateChannel(&xvfs_tclfs_channelType, Tcl_GetString(channelName), channelInstanceData, TCL_READABLE);
155: Tcl_DecrRefCount(channelName);
156: if (!channel) {
157: Tcl_DecrRefCount(path);
158: Tcl_Free((char *) channelInstanceData);
159:
160: return(NULL);
161: }
162:
163: channelInstanceData->channel = channel;
164:
165: return(channel);
166: }
167:
168: static int xvfs_tclfs_closeChannel(ClientData channelInstanceData_p, Tcl_Interp *interp);
169: static int xvfs_tclfs_closeChannelEvent(Tcl_Event *event_p, int flags) {
170: struct xvfs_tclfs_channel_id *channelInstanceData;
171: struct xvfs_tclfs_channel_event *event;
172:
173: event = (struct xvfs_tclfs_channel_event *) event_p;
174: channelInstanceData = event->channelInstanceData;
175:
176: channelInstanceData->queuedEvents--;
177:
178: xvfs_tclfs_closeChannel((ClientData) channelInstanceData, NULL);
179:
180: return(1);
181: }
182:
183: static int xvfs_tclfs_closeChannel(ClientData channelInstanceData_p, Tcl_Interp *interp) {
184: struct xvfs_tclfs_channel_id *channelInstanceData;
185: struct xvfs_tclfs_channel_event *event;
186:
187: channelInstanceData = (struct xvfs_tclfs_channel_id *) channelInstanceData_p;
188:
189: channelInstanceData->closed = 1;
190:
191: if (channelInstanceData->queuedEvents != 0) {
192: event = (struct xvfs_tclfs_channel_event *) Tcl_Alloc(sizeof(*event));
193: event->tcl.proc = xvfs_tclfs_closeChannelEvent;
194: event->tcl.nextPtr = NULL;
195: event->channelInstanceData = channelInstanceData;
196:
197: channelInstanceData->queuedEvents++;
198:
199: Tcl_QueueEvent((Tcl_Event *) event, TCL_QUEUE_TAIL);
200:
201: return(0);
202: }
203:
204: Tcl_DecrRefCount(channelInstanceData->path);
205: Tcl_Free((char *) channelInstanceData);
206:
207: return(0);
208: }
209:
210: static int xvfs_tclfs_readChannel(ClientData channelInstanceData_p, char *buf, int bufSize, int *errorCodePtr) {
211: struct xvfs_tclfs_channel_id *channelInstanceData;
212: const unsigned char *data;
213: Tcl_WideInt offset, length;
214: char *path;
215:
216: channelInstanceData = (struct xvfs_tclfs_channel_id *) channelInstanceData_p;
217:
218: /*
219: * If we are already at the end of the file we can skip
220: * attempting to read it
221: */
222: if (channelInstanceData->eofMarked) {
223: return(0);
224: }
225:
226: path = Tcl_GetString(channelInstanceData->path);
227: offset = channelInstanceData->currentOffset;
228: length = bufSize;
229:
230: data = channelInstanceData->fsInstanceInfo->fsInfo->getDataProc(path, offset, &length);
231:
232: if (length < 0) {
233: *errorCodePtr = xvfs_errorToErrno(length);
234:
235: return(-1);
236: }
237:
238: if (length == 0) {
239: channelInstanceData->eofMarked = 1;
240: } else {
241: memcpy(buf, data, length);
242:
243: channelInstanceData->currentOffset += length;
244: }
245:
246: return(length);
247: }
248:
249: static int xvfs_tclfs_watchChannelEvent(Tcl_Event *event_p, int flags) {
250: struct xvfs_tclfs_channel_id *channelInstanceData;
251: struct xvfs_tclfs_channel_event *event;
252:
253: event = (struct xvfs_tclfs_channel_event *) event_p;
254: channelInstanceData = event->channelInstanceData;
255:
256: channelInstanceData->queuedEvents--;
257:
258: if (channelInstanceData->closed) {
259: return(1);
260: }
261:
262: Tcl_NotifyChannel(channelInstanceData->channel, TCL_READABLE);
263:
264: return(1);
265: }
266:
267: static void xvfs_tclfs_watchChannel(ClientData channelInstanceData_p, int mask) {
268: struct xvfs_tclfs_channel_id *channelInstanceData;
269: struct xvfs_tclfs_channel_event *event;
270:
271: if ((mask & TCL_READABLE) != TCL_READABLE) {
272: return;
273: }
274:
275: channelInstanceData = (struct xvfs_tclfs_channel_id *) channelInstanceData_p;
276:
277: /*
278: * If the read call has marked that we have reached EOF,
279: * do not signal any further
280: */
281: if (channelInstanceData->eofMarked) {
282: return;
283: }
284:
285: event = (struct xvfs_tclfs_channel_event *) Tcl_Alloc(sizeof(*event));
286: event->tcl.proc = xvfs_tclfs_watchChannelEvent;
287: event->tcl.nextPtr = NULL;
288: event->channelInstanceData = channelInstanceData;
289:
290: channelInstanceData->queuedEvents++;
291:
292: Tcl_QueueEvent((Tcl_Event *) event, TCL_QUEUE_TAIL);
293:
294: return;
295: }
296:
297: static int xvfs_tclfs_seekChannel(ClientData channelInstanceData_p, long offset, int mode, int *errorCodePtr) {
298: struct xvfs_tclfs_channel_id *channelInstanceData;
299: Tcl_WideInt newOffset, fileSize;
300:
301: channelInstanceData = (struct xvfs_tclfs_channel_id *) channelInstanceData_p;
302:
303: newOffset = channelInstanceData->currentOffset;
304: fileSize = channelInstanceData->fileSize;
305:
306: switch (mode) {
307: case SEEK_CUR:
308: newOffset += offset;
309: break;
310: case SEEK_SET:
311: newOffset = offset;
312: break;
313: case SEEK_END:
314: newOffset = fileSize + offset;
315: break;
316: default:
317: *errorCodePtr = xvfs_errorToErrno(XVFS_RV_ERR_EINVAL);
318:
319: return(-1);
320: }
321:
322: /*
323: * We allow users to seek right up to the end of the buffer, but
324: * no further, this way if they want to seek backwards from there
325: * it is possible to do so.
326: */
327: if (newOffset < 0 || newOffset > fileSize) {
328: *errorCodePtr = xvfs_errorToErrno(XVFS_RV_ERR_EINVAL);
329:
330: return(-1);
331: }
332:
333: if (newOffset != channelInstanceData->currentOffset) {
334: channelInstanceData->eofMarked = 0;
335: channelInstanceData->currentOffset = newOffset;
336: }
337:
338: return(channelInstanceData->currentOffset);
339: }
340:
341: static void xvfs_tclfs_prepareChannelType(void) {
342: xvfs_tclfs_channelType.typeName = "xvfs";
343: xvfs_tclfs_channelType.version = TCL_CHANNEL_VERSION_2;
344: xvfs_tclfs_channelType.closeProc = xvfs_tclfs_closeChannel;
345: xvfs_tclfs_channelType.inputProc = xvfs_tclfs_readChannel;
346: xvfs_tclfs_channelType.outputProc = NULL;
347: xvfs_tclfs_channelType.watchProc = xvfs_tclfs_watchChannel;
348: xvfs_tclfs_channelType.getHandleProc = NULL;
349: xvfs_tclfs_channelType.seekProc = xvfs_tclfs_seekChannel;
350: xvfs_tclfs_channelType.setOptionProc = NULL;
351: xvfs_tclfs_channelType.getOptionProc = NULL;
352: xvfs_tclfs_channelType.close2Proc = NULL;
353: xvfs_tclfs_channelType.blockModeProc = NULL;
354: xvfs_tclfs_channelType.flushProc = NULL;
355: xvfs_tclfs_channelType.handlerProc = NULL;
356: xvfs_tclfs_channelType.wideSeekProc = NULL;
357: xvfs_tclfs_channelType.threadActionProc = NULL;
358: xvfs_tclfs_channelType.truncateProc = NULL;
359: }
360:
361: /*
362: * Internal Tcl_Filesystem functions, with the appropriate instance info
363: */
364: static int xvfs_tclfs_pathInFilesystem(Tcl_Obj *path, ClientData *dataPtr, struct xvfs_tclfs_instance_info *instanceInfo) {
365: const char *relativePath;
366:
367: relativePath = xvfs_relativePath(path, instanceInfo);
368: if (!relativePath) {
369: return(-1);
370: }
371:
372: return(TCL_OK);
373: }
374:
375: static int xvfs_tclfs_stat(Tcl_Obj *path, Tcl_StatBuf *statBuf, struct xvfs_tclfs_instance_info *instanceInfo) {
376: const char *pathStr;
377: int retval;
378:
379: pathStr = xvfs_relativePath(path, instanceInfo);
380:
381: retval = instanceInfo->fsInfo->getStatProc(pathStr, statBuf);
382: if (retval < 0) {
383: retval = -1;
384: }
385:
386: return(retval);
387: }
388:
389: static Tcl_Obj *xvfs_tclfs_listVolumes(struct xvfs_tclfs_instance_info *instanceInfo) {
390: return(NULL);
391: }
392:
393: static Tcl_Channel xvfs_tclfs_openFileChannel(Tcl_Interp *interp, Tcl_Obj *path, int mode, int permissions, struct xvfs_tclfs_instance_info *instanceInfo) {
394: const char *pathStr;
395:
396: pathStr = xvfs_relativePath(path, instanceInfo);
397:
398: if (mode & O_WRONLY) {
399: return(NULL);
400: }
401:
402: return(xvfs_tclfs_openChannel(Tcl_NewStringObj(pathStr, -1), instanceInfo));
403: }
404: #endif /* XVFS_MODE_SERVER || XVFS_MODE_STANDALONE || XVFS_MODE_FLEIXBLE */
405:
406: #if defined(XVFS_MODE_STANDALONE) || defined(XVFS_MODE_FLEXIBLE)
407: /*
408: * Tcl_Filesystem handlers for the standalone implementation
409: */
410: static struct xvfs_tclfs_instance_info xvfs_tclfs_standalone_info;
411: static int xvfs_tclfs_standalone_pathInFilesystem(Tcl_Obj *path, ClientData *dataPtr) {
412: return(xvfs_tclfs_pathInFilesystem(path, dataPtr, &xvfs_tclfs_standalone_info));
413: }
414:
415: static int xvfs_tclfs_standalone_stat(Tcl_Obj *path, Tcl_StatBuf *statBuf) {
416: return(xvfs_tclfs_stat(path, statBuf, &xvfs_tclfs_standalone_info));
417: }
418:
419: static Tcl_Obj *xvfs_tclfs_standalone_listVolumes(void) {
420: return(xvfs_tclfs_listVolumes(&xvfs_tclfs_standalone_info));
421: }
422:
423: static Tcl_Channel xvfs_tclfs_standalone_openFileChannel(Tcl_Interp *interp, Tcl_Obj *path, int mode, int permissions) {
424: return(xvfs_tclfs_openFileChannel(interp, path, mode, permissions, &xvfs_tclfs_standalone_info));
425: }
426:
427: /*
428: * There are three (3) modes of operation for Xvfs_Register:
429: * 1. standalone -- We register our own Tcl_Filesystem
430: * and handle requests under `//xvfs:/<fsName>`
431: * 2. client -- A single Tcl_Filesystem is registered for the
432: * interp to handle requests under `//xvfs:/` which
433: * then dispatches to the appropriate registered
434: * handler
435: * 3. flexible -- Attempts to find a core Xvfs instance for the
436: * process at runtime, if found do #2, otherwise
437: * fallback to #1
438: *
439: */
440: static Tcl_Filesystem xvfs_tclfs_standalone_fs;
441: static int xvfs_standalone_register(Tcl_Interp *interp, struct Xvfs_FSInfo *fsInfo) {
442: int tcl_ret;
443: static int registered = 0;
444:
445: /*
446: * Ensure this instance is not already registered
447: */
448: if (registered) {
449: return(TCL_OK);
450: }
451: registered = 1;
452:
453: /*
454: * In standalone mode, we only support the same protocol we are
455: * compiling for.
456: */
457: if (fsInfo->protocolVersion != XVFS_PROTOCOL_VERSION) {
458: if (interp) {
459: Tcl_SetResult(interp, "Protocol mismatch", NULL);
460: }
461: return(TCL_ERROR);
462: }
463:
464: xvfs_tclfs_standalone_fs.typeName = "xvfs";
465: xvfs_tclfs_standalone_fs.structureLength = sizeof(xvfs_tclfs_standalone_fs);
466: xvfs_tclfs_standalone_fs.version = TCL_FILESYSTEM_VERSION_1;
467: xvfs_tclfs_standalone_fs.pathInFilesystemProc = xvfs_tclfs_standalone_pathInFilesystem;
468: xvfs_tclfs_standalone_fs.dupInternalRepProc = NULL;
469: xvfs_tclfs_standalone_fs.freeInternalRepProc = NULL;
470: xvfs_tclfs_standalone_fs.internalToNormalizedProc = NULL;
471: xvfs_tclfs_standalone_fs.createInternalRepProc = NULL;
472: xvfs_tclfs_standalone_fs.normalizePathProc = NULL;
473: xvfs_tclfs_standalone_fs.filesystemPathTypeProc = NULL;
474: xvfs_tclfs_standalone_fs.filesystemSeparatorProc = NULL;
475: xvfs_tclfs_standalone_fs.statProc = xvfs_tclfs_standalone_stat;
476: xvfs_tclfs_standalone_fs.accessProc = NULL;
477: xvfs_tclfs_standalone_fs.openFileChannelProc = xvfs_tclfs_standalone_openFileChannel;
aa08a4a749 2019-09-14 478: xvfs_tclfs_standalone_fs.matchInDirectoryProc = NULL; /* XXX:TODO */
479: xvfs_tclfs_standalone_fs.utimeProc = NULL;
480: xvfs_tclfs_standalone_fs.linkProc = NULL;
481: xvfs_tclfs_standalone_fs.listVolumesProc = xvfs_tclfs_standalone_listVolumes;
482: xvfs_tclfs_standalone_fs.fileAttrStringsProc = NULL;
483: xvfs_tclfs_standalone_fs.fileAttrsGetProc = NULL;
484: xvfs_tclfs_standalone_fs.fileAttrsSetProc = NULL;
485: xvfs_tclfs_standalone_fs.createDirectoryProc = NULL;
486: xvfs_tclfs_standalone_fs.removeDirectoryProc = NULL;
487: xvfs_tclfs_standalone_fs.deleteFileProc = NULL;
488: xvfs_tclfs_standalone_fs.copyFileProc = NULL;
489: xvfs_tclfs_standalone_fs.renameFileProc = NULL;
490: xvfs_tclfs_standalone_fs.copyDirectoryProc = NULL;
491: xvfs_tclfs_standalone_fs.lstatProc = NULL;
492: xvfs_tclfs_standalone_fs.loadFileProc = NULL;
493: xvfs_tclfs_standalone_fs.getCwdProc = NULL;
494: xvfs_tclfs_standalone_fs.chdirProc = NULL;
495:
496: xvfs_tclfs_standalone_info.fsInfo = fsInfo;
497: xvfs_tclfs_standalone_info.mountpoint = Tcl_NewObj();
498: Tcl_AppendStringsToObj(xvfs_tclfs_standalone_info.mountpoint, XVFS_ROOT_MOUNTPOINT, fsInfo->name, NULL);
499:
500: tcl_ret = Tcl_FSRegister(NULL, &xvfs_tclfs_standalone_fs);
501: if (tcl_ret != TCL_OK) {
502: if (interp) {
503: Tcl_SetResult(interp, "Tcl_FSRegister() failed", NULL);
504: }
505:
506: return(tcl_ret);
507: }
508:
509: xvfs_tclfs_prepareChannelType();
510:
511: return(TCL_OK);
512: }
513: #endif /* XVFS_MODE_STANDALONE || XVFS_MODE_FLEXIBLE */
514:
515: #if defined(XVFS_MODE_FLEXIBLE)
516: static int xvfs_flexible_register(Tcl_Interp *interp, struct Xvfs_FSInfo *fsInfo) {
517: ClientData fsHandlerDataRaw;
518: struct xvfs_tclfs_server_info *fsHandlerData;
519: const Tcl_Filesystem *fsHandler;
520: int (*xvfs_register)(Tcl_Interp *interp, struct Xvfs_FSInfo *fsInfo);
521: Tcl_Obj *rootPathObj;
522:
523: xvfs_register = &xvfs_standalone_register;
524:
525: rootPathObj = Tcl_NewStringObj(XVFS_ROOT_MOUNTPOINT, -1);
526: if (!rootPathObj) {
527: return(xvfs_register(interp, fsInfo));
528: }
529:
530: Tcl_IncrRefCount(rootPathObj);
531: fsHandler = Tcl_FSGetFileSystemForPath(rootPathObj);
532: Tcl_DecrRefCount(rootPathObj);
533:
534: if (!fsHandler) {
535: return(xvfs_register(interp, fsInfo));
536: }
537:
538: fsHandlerDataRaw = Tcl_FSData(fsHandler);
539: if (!fsHandlerDataRaw) {
540: return(xvfs_register(interp, fsInfo));
541: }
542:
543: fsHandlerData = (struct xvfs_tclfs_server_info *) fsHandlerDataRaw;
544:
545: /*
546: * XXX:TODO: What is the chance that the handler for //xvfs:/ hold
547: * client data smaller than XVFS_INTERNAL_SERVER_MAGIC_LEN ?
548: */
549: if (memcmp(fsHandlerData->magic, XVFS_INTERNAL_SERVER_MAGIC, sizeof(fsHandlerData->magic)) == 0) {
550: xvfs_register = fsHandlerData->registerProc;
551: }
552:
553: return(xvfs_register(interp, fsInfo));
554: }
555: #endif /* XVFS_MODE_FLEXIBLE */
556:
557: #if defined(XVFS_MODE_SERVER)
558: int Xvfs_Register(Tcl_Interp *interp, struct Xvfs_FSInfo *fsInfo) {
559: return(TCL_ERROR);
560: }
561: #endif /* XVFS_MODE_SERVER */