Check-in [d5bfe6710c]
Overview
Comment:Added more debugging, renamed appfs_terminate_interp for with more accurate name, fixed FUSE read/write to use pread/pwrite and return no short reads as is required by FUSE, added call to Tcl_FinalizeThread() on thread termination
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:d5bfe6710cf4e957ef16d3e5aabea6bc3fbf9ec7
User & Date: rkeene on 2014-11-15 16:46:19
Other Links: manifest | tags
Context
2014-11-15
17:20
Updated to flush cache before exiting in exit path mode and allow short reads without returning an error as they seem to be expected check-in: 67735b9ee3 user: rkeene tags: trunk
16:46
Added more debugging, renamed appfs_terminate_interp for with more accurate name, fixed FUSE read/write to use pread/pwrite and return no short reads as is required by FUSE, added call to Tcl_FinalizeThread() on thread termination check-in: d5bfe6710c user: rkeene tags: trunk
2014-11-14
21:46
Updated to support unthreaded Tcl and fixed a memory leak with home directory determination check-in: 6af0168ed8 user: rkeene tags: trunk
Changes

Modified appfsd.c from [1768754e26] to [69177869fa].

21
22
23
24
25
26
27
28


















29
30
31
32
33
34
35
..
59
60
61
62
63
64
65

66
67
68
69
70
71
72
...
377
378
379
380
381
382
383


384
385
386
387
388
389
390
...
405
406
407
408
409
410
411


412
413
414
415
416
417
418
...
474
475
476
477
478
479
480


481
482
483
484
485
486
487
...
496
497
498
499
500
501
502


503
504
505
506
507
508
509
...
670
671
672
673
674
675
676

677
678
679
680
681
682
683
...
773
774
775
776
777
778
779


780
781
782
783


784
785
786
787
788
789
790
791


792
793
794
795
796
797
798
...
842
843
844
845
846
847
848


849
850
851
852
853
854
855
...
935
936
937
938
939
940
941


942
943
944
945
946
947
948
....
1091
1092
1093
1094
1095
1096
1097






1098
1099
1100
1101
1102
1103
1104
....
1161
1162
1163
1164
1165
1166
1167


1168
1169
1170
1171
1172
1173
1174
....
1220
1221
1222
1223
1224
1225
1226


1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239


1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250


1251
1252
1253
1254
1255


1256
1257
1258
1259
1260
1261
1262
....
1273
1274
1275
1276
1277
1278
1279


1280
1281
1282
1283
1284
1285
1286
1287


1288
1289
1290
1291
1292
1293
1294
1295
....
1296
1297
1298
1299
1300
1301
1302


1303
1304
1305
1306
1307
1308
1309
1310
1311

1312
1313
1314
1315
1316







1317
1318
1319
1320

1321















1322
1323
1324
1325
1326
1327

1328
1329
1330
1331
1332
1333
1334







1335
1336
1337
1338

1339

1340










1341
1342
1343
1344
1345
1346
1347
....
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
....
1782
1783
1784
1785
1786
1787
1788


1789
1790
1791
1792
1793
1794
1795
....
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
 */
#ifndef APPFS_CACHEDIR
#define APPFS_CACHEDIR "/var/cache/appfs"
#endif

/* Debugging macros */
#ifdef DEBUG
#define APPFS_DEBUG(x...) { fprintf(stderr, "[debug] %s:%i:%s: ", __FILE__, __LINE__, __func__); fprintf(stderr, x); fprintf(stderr, "\n"); }


















#else
#define APPFS_DEBUG(x...) /**/
#endif

/*
 * SHA1 Tcl Package initializer, from sha1.o
 */
................................................................................
/*
 * Handle unthreaded Tcl
 */
pthread_mutex_t appfs_tcl_big_global_lock = PTHREAD_MUTEX_INITIALIZER;
#define appfs_call_libtcl_enter pthread_mutex_lock(&appfs_tcl_big_global_lock);
#define appfs_call_libtcl_exit pthread_mutex_unlock(&appfs_tcl_big_global_lock);
#else

#define appfs_call_libtcl_enter /**/
#define appfs_call_libtcl_exit /**/
#endif
#define appfs_call_libtcl(x...) appfs_call_libtcl_enter x appfs_call_libtcl_exit

/*
 * Global variables for AppFS Tcl Interpreter restarting
................................................................................

	thread_interp_reset_key = global_interp_reset_key;

	if (interp == NULL) {
		interp = appfs_create_TclInterp(NULL);

		if (interp == NULL) {


			return(NULL);
		}

		pthread_ret = pthread_setspecific(interpKey, interp);
		if (pthread_ret != 0) {
			APPFS_DEBUG("pthread_setspecific() failed.  Terminating Tcl interpreter.");

................................................................................
	Tcl_Obj **objv;
	const char *arg;
	va_list argp;
	int retval;
	int i;

	if (interp == NULL) {


		return(TCL_ERROR);
	}

	objv = (void *) ckalloc(sizeof(*objv) * objc);

	appfs_call_libtcl(
		objv[0] = Tcl_NewStringObj(cmd, -1);
................................................................................
		return(getuid());
	}

	ctx = fuse_get_context();
	if (ctx == NULL) {
		/* Unable to lookup user for some reason */
		/* Return an unprivileged user ID */


		return(1);
	}

	return(ctx->uid);
}

/*
................................................................................
		return(getgid());
	}

	ctx = fuse_get_context();
	if (ctx == NULL) {
		/* Unable to lookup user for some reason */
		/* Return an unprivileged user ID */


		return(1);
	}

	return(ctx->gid);
}

static void appfs_simulate_user_fs_enter(void) {
................................................................................

	pthread_ret = pthread_mutex_unlock(&appfs_path_info_cache_mutex);
	if (pthread_ret != 0) {
		APPFS_DEBUG("Unable to unlock path_info cache mutex !");

		return;
	}

	return;
}

static void appfs_get_path_info_cache_rm(const char *path, uid_t uid) {
	unsigned int hash_idx;
	int pthread_ret;

................................................................................
	retval = 0;

	fsuid = appfs_get_fsuid();

	cache_ret = appfs_get_path_info_cache_get(path, fsuid, pathinfo);
	if (cache_ret == 0) {
		if (pathinfo->type == APPFS_PATHTYPE_DOES_NOT_EXIST) {


			return(-ENOENT);
		}

		if (pathinfo->type == APPFS_PATHTYPE_INVALID) {


			return(-EIO);
		}

		return(0);
	}

	interp = appfs_TclInterp();
	if (interp == NULL) {


		return(-EIO);
	}

	appfs_call_libtcl(Tcl_Preserve(interp);)

	tcl_ret = appfs_Tcl_Eval(interp, 2, "::appfs::getattr", path);
	if (tcl_ret != TCL_OK) {
................................................................................

		appfs_call_libtcl(Tcl_Release(interp);)

		return(-EIO);
	}

	if (attr_value == NULL) {


		appfs_call_libtcl(Tcl_Release(interp);)

		return(-EIO);
	}

	pathinfo->packaged = 0;
	pathinfo->inode = appfs_get_path_inode(path);
................................................................................
		}

		Tcl_Release(interp);
	)

	if (retval == 0) {
		appfs_get_path_info_cache_add(path, fsuid, pathinfo);


	}

	return(retval);
}

static char *appfs_prepare_to_create(const char *path) {
	Tcl_Interp *interp;
................................................................................
	}
#endif

	pathinfo.type = APPFS_PATHTYPE_INVALID;

	retval = appfs_get_path_info(path, &pathinfo);
	if (retval != 0) {






		return(retval);
	}

	memset(stbuf, 0, sizeof(struct stat));

	stbuf->st_mtime = pathinfo.time;
	stbuf->st_ctime = pathinfo.time;
................................................................................
	int children_count, idx;
	int tcl_ret;

	APPFS_DEBUG("Enter (path = %s, ...)", path);

	interp = appfs_TclInterp();
	if (interp == NULL) {


		return(0);
	}

	appfs_call_libtcl(Tcl_Preserve(interp);)

	filler(buf, ".", NULL, 0);
	filler(buf, "..", NULL, 0);
................................................................................
	APPFS_DEBUG("Enter (path = %s, ...)", path);

	gpi_ret = appfs_get_path_info(path, &pathinfo);

	if ((fi->flags & (O_WRONLY|O_CREAT)) == (O_CREAT|O_WRONLY)) {
		/* The file will be created if it does not exist */
		if (gpi_ret != 0 && gpi_ret != -ENOENT) {


			return(gpi_ret);
		}

		mode = "create";

		/*
		 * We have to clear the cache here so that the number of
		 * links gets maintained on the parent directory
		 */
		appfs_get_path_info_cache_flush(appfs_get_fsuid(), -1);
	} else {
		/* The file must already exist */
		if (gpi_ret != 0) {


			return(gpi_ret);
		}

		mode = "";

		if ((fi->flags & O_WRONLY) == O_WRONLY) {
			mode = "write";
		}
	}

	if (pathinfo.type == APPFS_PATHTYPE_DIRECTORY) {


		return(-EISDIR);
	}

	interp = appfs_TclInterp();
	if (interp == NULL) {


		return(-EIO);
	}

	appfs_call_libtcl(Tcl_Preserve(interp);)

	tcl_ret = appfs_Tcl_Eval(interp, 3, "::appfs::openpath", path, mode);
	if (tcl_ret != TCL_OK) {
................................................................................
	appfs_call_libtcl(
		real_path = Tcl_GetStringResult(interp);
	)

	appfs_call_libtcl(Tcl_Release(interp);)

	if (real_path == NULL) {


		return(-EIO);
	}

	APPFS_DEBUG("Translated request to open %s to opening %s (mode = \"%s\")", path, real_path, mode);

	fh = open(real_path, fi->flags, 0600);

	if (fh < 0) {


		return(-EIO);
	}

	fi->fh = fh;

	return(0);
}

................................................................................
static int appfs_fuse_close(const char *path, struct fuse_file_info *fi) {
	int close_ret;

	appfs_get_path_info_cache_rm(path, appfs_get_fsuid());

	close_ret = close(fi->fh);
	if (close_ret != 0) {


		return(-EIO);
	}

	return(0);
}

static int appfs_fuse_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
	off_t lseek_ret;
	ssize_t read_ret;


	APPFS_DEBUG("Enter (path = %s, ...)", path);

	lseek_ret = lseek(fi->fh, offset, SEEK_SET);
	if (lseek_ret != offset) {







		return(-EIO);
	}

	read_ret = read(fi->fh, buf, size);

















	return(read_ret);
}

static int appfs_fuse_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
	off_t lseek_ret;
	ssize_t write_ret;


	APPFS_DEBUG("Enter (path = %s, ...)", path);

	appfs_get_path_info_cache_rm(path, appfs_get_fsuid());

	lseek_ret = lseek(fi->fh, offset, SEEK_SET);
	if (lseek_ret != offset) {







		return(-EIO);
	}

	write_ret = write(fi->fh, buf, size);



	return(write_ret);










}

static int appfs_fuse_mknod(const char *path, mode_t mode, dev_t device) {
	char *real_path;
	int mknod_ret;

	APPFS_DEBUG("Enter (path = %s, ...)", path);
................................................................................

	return;
}

/*
 * Terminate a thread
 */
static void appfs_terminate_interp(void *_interp) {
	Tcl_Interp *interp;

	APPFS_DEBUG("Called: _interp = %p", _interp);

	if (_interp == NULL) {
		APPFS_DEBUG("Terminating thread with no interpreter");

................................................................................
	interp = _interp;

	APPFS_DEBUG("Terminating interpreter due to thread termination");

	appfs_call_libtcl(
		Tcl_DeleteInterp(interp);
	)



	return;
}

/*
 * FUSE operations structure
 */
................................................................................
	Tcl_StaticPackage(NULL, "appfsd", Appfsd_Init, NULL);

	/*
	 * Create a thread-specific-data (TSD) key for each thread to refer
	 * to its own Tcl interpreter.  Tcl interpreters must be unique per
	 * thread and new threads are dynamically created by FUSE.
	 */
	pthread_ret = pthread_key_create(&interpKey, appfs_terminate_interp);
	if (pthread_ret != 0) {
		fprintf(stderr, "Unable to create TSD key for Tcl.  Aborting.\n");

		return(1);
	}

	/*







|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>







 







>
>







 







>
>







 







>
>







 







>
>







 







>







 







>
>




>
>








>
>







 







>
>







 







>
>







 







>
>
>
>
>
>







 







>
>







 







>
>













>
>











>
>





>
>







 







>
>








>
>
|







 







>
>
|






<

>

|

|
<
>
>
>
>
>
>
>
|
|

|
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|



<

>





|
<
>
>
>
>
>
>
>
|
|

|
>
|
>
|
>
>
>
>
>
>
>
>
>
>







 







|







 







>
>







 







|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
..
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
...
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
...
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
...
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
...
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
...
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
...
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
...
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
...
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
....
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
....
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
....
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
....
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
....
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369

1370
1371
1372
1373
1374
1375

1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407

1408
1409
1410
1411
1412
1413
1414
1415

1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
....
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
....
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
....
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
 */
#ifndef APPFS_CACHEDIR
#define APPFS_CACHEDIR "/var/cache/appfs"
#endif

/* Debugging macros */
#ifdef DEBUG
int appfs_debug_fd = STDERR_FILENO;
#define APPFS_DEBUG(x...) { \
	char buf[8192]; \
	int bufoff = 0; \
	if (appfs_debug_fd == -1) { \
		appfs_debug_fd = open("/tmp/appfsd.log", O_WRONLY | O_APPEND | O_CREAT, 0600); \
	}; \
	bufoff = snprintf(buf, sizeof(buf), "[debug] [t=%llx] %s:%i:%s: ", (unsigned long long) pthread_self(), __FILE__, __LINE__, __func__); \
	if (bufoff < sizeof(buf)) { \
		bufoff += snprintf(buf + bufoff, sizeof(buf) - bufoff, x); \
	}; \
	if (bufoff < sizeof(buf)) { \
		bufoff += snprintf(buf + bufoff, sizeof(buf) - bufoff, "\n");\
	} \
	if (bufoff > sizeof(buf)) { \
		bufoff = sizeof(buf); \
	}; \
	write(appfs_debug_fd, buf, bufoff); \
}
#else
#define APPFS_DEBUG(x...) /**/
#endif

/*
 * SHA1 Tcl Package initializer, from sha1.o
 */
................................................................................
/*
 * Handle unthreaded Tcl
 */
pthread_mutex_t appfs_tcl_big_global_lock = PTHREAD_MUTEX_INITIALIZER;
#define appfs_call_libtcl_enter pthread_mutex_lock(&appfs_tcl_big_global_lock);
#define appfs_call_libtcl_exit pthread_mutex_unlock(&appfs_tcl_big_global_lock);
#else
#warning Using a Threaded Tcl interpreter may cause memory leaks
#define appfs_call_libtcl_enter /**/
#define appfs_call_libtcl_exit /**/
#endif
#define appfs_call_libtcl(x...) appfs_call_libtcl_enter x appfs_call_libtcl_exit

/*
 * Global variables for AppFS Tcl Interpreter restarting
................................................................................

	thread_interp_reset_key = global_interp_reset_key;

	if (interp == NULL) {
		interp = appfs_create_TclInterp(NULL);

		if (interp == NULL) {
			APPFS_DEBUG("Create interp failed, returningin failure.");

			return(NULL);
		}

		pthread_ret = pthread_setspecific(interpKey, interp);
		if (pthread_ret != 0) {
			APPFS_DEBUG("pthread_setspecific() failed.  Terminating Tcl interpreter.");

................................................................................
	Tcl_Obj **objv;
	const char *arg;
	va_list argp;
	int retval;
	int i;

	if (interp == NULL) {
		APPFS_DEBUG("Invalid interpreter passed in, returning in failure.");

		return(TCL_ERROR);
	}

	objv = (void *) ckalloc(sizeof(*objv) * objc);

	appfs_call_libtcl(
		objv[0] = Tcl_NewStringObj(cmd, -1);
................................................................................
		return(getuid());
	}

	ctx = fuse_get_context();
	if (ctx == NULL) {
		/* Unable to lookup user for some reason */
		/* Return an unprivileged user ID */
		APPFS_DEBUG("Unable to lookup user for some reason, returninng user ID of 1");

		return(1);
	}

	return(ctx->uid);
}

/*
................................................................................
		return(getgid());
	}

	ctx = fuse_get_context();
	if (ctx == NULL) {
		/* Unable to lookup user for some reason */
		/* Return an unprivileged user ID */
		APPFS_DEBUG("Unable to lookup group for some reason, returninng group ID of 1");

		return(1);
	}

	return(ctx->gid);
}

static void appfs_simulate_user_fs_enter(void) {
................................................................................

	pthread_ret = pthread_mutex_unlock(&appfs_path_info_cache_mutex);
	if (pthread_ret != 0) {
		APPFS_DEBUG("Unable to unlock path_info cache mutex !");

		return;
	}

	return;
}

static void appfs_get_path_info_cache_rm(const char *path, uid_t uid) {
	unsigned int hash_idx;
	int pthread_ret;

................................................................................
	retval = 0;

	fsuid = appfs_get_fsuid();

	cache_ret = appfs_get_path_info_cache_get(path, fsuid, pathinfo);
	if (cache_ret == 0) {
		if (pathinfo->type == APPFS_PATHTYPE_DOES_NOT_EXIST) {
			APPFS_DEBUG("Returning from cache: does not exist \"%s\"", path);

			return(-ENOENT);
		}

		if (pathinfo->type == APPFS_PATHTYPE_INVALID) {
			APPFS_DEBUG("Returning from cache: invalid object \"%s\"", path);

			return(-EIO);
		}

		return(0);
	}

	interp = appfs_TclInterp();
	if (interp == NULL) {
		APPFS_DEBUG("error: Unable to get an interpreter");

		return(-EIO);
	}

	appfs_call_libtcl(Tcl_Preserve(interp);)

	tcl_ret = appfs_Tcl_Eval(interp, 2, "::appfs::getattr", path);
	if (tcl_ret != TCL_OK) {
................................................................................

		appfs_call_libtcl(Tcl_Release(interp);)

		return(-EIO);
	}

	if (attr_value == NULL) {
		APPFS_DEBUG("error: Unable to get type for \"%s\" from Tcl", path);

		appfs_call_libtcl(Tcl_Release(interp);)

		return(-EIO);
	}

	pathinfo->packaged = 0;
	pathinfo->inode = appfs_get_path_inode(path);
................................................................................
		}

		Tcl_Release(interp);
	)

	if (retval == 0) {
		appfs_get_path_info_cache_add(path, fsuid, pathinfo);
	} else {
		APPFS_DEBUG("error: Invalid type for \"%s\" from Tcl", path);
	}

	return(retval);
}

static char *appfs_prepare_to_create(const char *path) {
	Tcl_Interp *interp;
................................................................................
	}
#endif

	pathinfo.type = APPFS_PATHTYPE_INVALID;

	retval = appfs_get_path_info(path, &pathinfo);
	if (retval != 0) {
		if (retval == -ENOENT) {
			APPFS_DEBUG("get_path_info returned ENOENT, returning it as well.");
		} else {
			APPFS_DEBUG("error: get_path_info failed");
		}

		return(retval);
	}

	memset(stbuf, 0, sizeof(struct stat));

	stbuf->st_mtime = pathinfo.time;
	stbuf->st_ctime = pathinfo.time;
................................................................................
	int children_count, idx;
	int tcl_ret;

	APPFS_DEBUG("Enter (path = %s, ...)", path);

	interp = appfs_TclInterp();
	if (interp == NULL) {
		APPFS_DEBUG("error: Unable to get an interpreter");

		return(0);
	}

	appfs_call_libtcl(Tcl_Preserve(interp);)

	filler(buf, ".", NULL, 0);
	filler(buf, "..", NULL, 0);
................................................................................
	APPFS_DEBUG("Enter (path = %s, ...)", path);

	gpi_ret = appfs_get_path_info(path, &pathinfo);

	if ((fi->flags & (O_WRONLY|O_CREAT)) == (O_CREAT|O_WRONLY)) {
		/* The file will be created if it does not exist */
		if (gpi_ret != 0 && gpi_ret != -ENOENT) {
			APPFS_DEBUG("error: get_path_info failed");

			return(gpi_ret);
		}

		mode = "create";

		/*
		 * We have to clear the cache here so that the number of
		 * links gets maintained on the parent directory
		 */
		appfs_get_path_info_cache_flush(appfs_get_fsuid(), -1);
	} else {
		/* The file must already exist */
		if (gpi_ret != 0) {
			APPFS_DEBUG("error: get_path_info failed");

			return(gpi_ret);
		}

		mode = "";

		if ((fi->flags & O_WRONLY) == O_WRONLY) {
			mode = "write";
		}
	}

	if (pathinfo.type == APPFS_PATHTYPE_DIRECTORY) {
		APPFS_DEBUG("error: Asked to open a directory.");

		return(-EISDIR);
	}

	interp = appfs_TclInterp();
	if (interp == NULL) {
		APPFS_DEBUG("error: Unable to get an interpreter");

		return(-EIO);
	}

	appfs_call_libtcl(Tcl_Preserve(interp);)

	tcl_ret = appfs_Tcl_Eval(interp, 3, "::appfs::openpath", path, mode);
	if (tcl_ret != TCL_OK) {
................................................................................
	appfs_call_libtcl(
		real_path = Tcl_GetStringResult(interp);
	)

	appfs_call_libtcl(Tcl_Release(interp);)

	if (real_path == NULL) {
		APPFS_DEBUG("error: real_path was NULL.")

		return(-EIO);
	}

	APPFS_DEBUG("Translated request to open %s to opening %s (mode = \"%s\")", path, real_path, mode);

	fh = open(real_path, fi->flags, 0600);

	if (fh < 0) {
		APPFS_DEBUG("error: open failed");

		return(errno * -1);
	}

	fi->fh = fh;

	return(0);
}

................................................................................
static int appfs_fuse_close(const char *path, struct fuse_file_info *fi) {
	int close_ret;

	appfs_get_path_info_cache_rm(path, appfs_get_fsuid());

	close_ret = close(fi->fh);
	if (close_ret != 0) {
		APPFS_DEBUG("error: close failed");

		return(errno * -1);
	}

	return(0);
}

static int appfs_fuse_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {

	ssize_t read_ret;
	int retval;

	APPFS_DEBUG("Enter (path = %s, buf, %lli, %lli, fd=%lli)", path, (long long) size, (long long) offset, (long long) fi->fh);

	retval = 0;


	while (size != 0) {
		read_ret = pread(fi->fh, buf, size, offset);

		if (read_ret < 0) {
			APPFS_DEBUG("error: read failed");

			return(errno * -1);
		}

		if (read_ret == 0) {
			break;
		}

		size -= read_ret;
		buf  += read_ret;
		offset += read_ret;
		retval += read_ret;
	}

	if (size != 0) {
		APPFS_DEBUG("error: incomplete read (this is an error because FUSE will request the exact length of the file)");

		return(0);
	}

	APPFS_DEBUG("Returning: %i", retval);

	return(retval);
}

static int appfs_fuse_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {

	ssize_t write_ret;
	int retval;

	APPFS_DEBUG("Enter (path = %s, ...)", path);

	appfs_get_path_info_cache_rm(path, appfs_get_fsuid());

	retval = 0;


	while (size != 0) {
		write_ret = pwrite(fi->fh, buf, size, offset);

		if (write_ret < 0) {
			APPFS_DEBUG("error: write failed");

			return(errno * -1);
		}

		if (write_ret == 0) {
			break;
		}

		size -= write_ret;
		buf  += write_ret;
		offset += write_ret;
		retval += write_ret;
	}

	if (size != 0) {
		APPFS_DEBUG("error: incomplete write");
	}

	return(retval);
}

static int appfs_fuse_mknod(const char *path, mode_t mode, dev_t device) {
	char *real_path;
	int mknod_ret;

	APPFS_DEBUG("Enter (path = %s, ...)", path);
................................................................................

	return;
}

/*
 * Terminate a thread
 */
static void appfs_terminate_interp_and_thread(void *_interp) {
	Tcl_Interp *interp;

	APPFS_DEBUG("Called: _interp = %p", _interp);

	if (_interp == NULL) {
		APPFS_DEBUG("Terminating thread with no interpreter");

................................................................................
	interp = _interp;

	APPFS_DEBUG("Terminating interpreter due to thread termination");

	appfs_call_libtcl(
		Tcl_DeleteInterp(interp);
	)

	Tcl_FinalizeThread();

	return;
}

/*
 * FUSE operations structure
 */
................................................................................
	Tcl_StaticPackage(NULL, "appfsd", Appfsd_Init, NULL);

	/*
	 * Create a thread-specific-data (TSD) key for each thread to refer
	 * to its own Tcl interpreter.  Tcl interpreters must be unique per
	 * thread and new threads are dynamically created by FUSE.
	 */
	pthread_ret = pthread_key_create(&interpKey, appfs_terminate_interp_and_thread);
	if (pthread_ret != 0) {
		fprintf(stderr, "Unable to create TSD key for Tcl.  Aborting.\n");

		return(1);
	}

	/*