#include "DirHandle.h" #include "Workspace.h" #include <algorithm> #include <dirent.h> #include <fcntl.h> #include <fuse.h> #include <iostream> #include <string> #include <sys/attr.h> #include <sys/stat.h> #include <sys/xattr.h> #include <tclap/CmdLine.h> #include <unistd.h> #include <vector> using std::cout; using std::cerr; using std::endl; using std::string; using std::transform; using std::vector; Workspace ws; #define G_PREFIX "org" #define G_KAUTH_FILESEC_XATTR G_PREFIX ".apple.system.Security" #define A_PREFIX "com" #define A_KAUTH_FILESEC_XATTR A_PREFIX ".apple.system.Security" #define XATTR_APPLE_PREFIX "com.apple." //--------------------------------------------------------------------------- // Filesystem operations //--------------------------------------------------------------------------- static int fsclient_getattr(const char *path, struct stat *stbuf) { cout << "fsclient_getattr " << path << endl; int status = stat(ws.path(path).c_str(), stbuf); if (status == 0) { return 0; } else { return -errno; } } static int fsclient_open(const char *path, struct fuse_file_info *fi) { cout << "fsclient_open " << path << endl; int fd = open(ws.path(path).c_str(), fi->flags); if (fd >= 0) { fi->fh = fd; return 0; } else { return -errno; } } static int fsclient_flush(const char *path, struct fuse_file_info *fi) { cout << "fsclient_flush " << path << endl; int res; (void)path; res = close(dup(fi->fh)); if (res == -1) { return -errno; } return 0; } static int fsclient_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { cout << "fsclient_read " << path << "[" << size << "," << offset << "]" << endl; ssize_t num_read = pread(fi->fh, buf, size, offset); cout << "fsclient_read num_read[" << num_read << "]" << endl; if (num_read == -1) { return -errno; } return num_read; } static int fsclient_release(const char *path, struct fuse_file_info *fi) { cout << "fsclient_release " << path << endl; (void) path; int status = close(fi->fh); if (status == 0) { return 0; } else { return -errno; } } static int fsclient_rename(const char *from, const char *to) { cout << "fsclient_rename " << from << " -> " << to << endl; int res; // TODO let the workspace class handle this? What does the P4API do with rename ws.rename(from, to); res = rename(ws.path(from).c_str(), ws.path(to).c_str()); if (res == -1) { return -errno; } return 0; } static int fsclient_mknod(const char *path, mode_t mode, dev_t rdev) { cout << "fsclient_mknod " << path << endl; int res; if (S_ISFIFO(mode)) { res = mkfifo(ws.path(path).c_str(), mode); } else { res = mknod(ws.path(path).c_str(), mode, rdev); } if (res == -1) { return -errno; } return 0; } // Right now, we assign the DIR* to the fi->fh field. static int fsclient_opendir(const char *path, struct fuse_file_info *fi) { cout << "opendir " << path << " ws.path[" << ws.path(path) << "]" << endl; DIR *dir = opendir(ws.path(path).c_str()); cout << "opendir " << path << " DIR[" << dir << "]" << endl; if (dir == NULL) { return -errno; } else { // Create "DirHandle" instance. DirHandle *handle = new DirHandle(dir); cout << "opendir " << path << " handle[" << handle << "]" << endl; fi->fh = reinterpret_cast<unsigned long>(handle); return 0; } } static int fsclient_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { (void) path; // Will not be used. DirHandle *dirHandle = reinterpret_cast<DirHandle *>(fi->fh); if (offset != dirHandle->offset) { seekdir(dirHandle->dir, offset); dirHandle->entry = NULL; dirHandle->offset = offset; } while (1) { struct stat st; off_t nextoff; if (!dirHandle->entry) { // Note: I'm not sure how to detect if this failed or is just at // the end. It returns NULL in both cases. dirHandle->entry = readdir(dirHandle->dir); if (!dirHandle->entry) break; } memset(&st, 0, sizeof(st)); st.st_ino = dirHandle->entry->d_ino; st.st_mode = DTTOIF(dirHandle->entry->d_type); nextoff = telldir(dirHandle->dir); if (filler(buf, dirHandle->entry->d_name, &st, nextoff)) break; dirHandle->entry = NULL; dirHandle->offset = nextoff; } return 0; } static int fsclient_releasedir(const char *path, struct fuse_file_info *fi) { (void) path; // Will not be used. DirHandle *dirHandle = reinterpret_cast<DirHandle *>(fi->fh); cout << "releasedir " << path << " dirHandle[" << dirHandle << "]" << endl; closedir(dirHandle->dir); delete dirHandle; return 0; } static int fsclient_write(const char *path, const char *buf, size_t nbyte, off_t offset, struct fuse_file_info *fi) { cout << "fsclient_write " << path << endl; ssize_t num_written = pwrite(fi->fh, buf, nbyte, offset); if (num_written == -1) { return -errno; } int error = ws.edit(path); if (error) { return -error; } return num_written; } static int fsclient_create(const char *path, mode_t mode, struct fuse_file_info *fi) { cout << "fsclient_create " << path << endl; int error = ws.add(path); if (error) { return -error; } int fd = open(ws.path(path).c_str(), fi->flags, mode); if (fd == -1) { return -errno; } fi->fh = fd; return 0; } static int fsclient_mkdir(const char *path, mode_t mode) { cout << "fsclient_mkdir " << path << endl; int err = mkdir(ws.path(path).c_str(), mode); if (err == -1) { return -errno; } return 0; } static int fsclient_unlink(const char *path) { cout << "fsclient_unlink " << path << endl; int err = unlink(ws.path(path).c_str()); if (err == -1) { return -errno; } return 0; } // Note: This is pretty much the same as the fsclient example implementation. // We need to do the same thing, and, there are a lot of macros in use that // are not really documented anywhere. static int fsclient_setattr_x(const char *path, struct setattr_x *attr) { cout << "fsclient_setattr_x " << path << endl; int res; uid_t uid = -1; gid_t gid = -1; if (SETATTR_WANTS_MODE(attr)) { res = lchmod(path, attr->mode); if (res == -1) { return -errno; } } if (SETATTR_WANTS_UID(attr)) { uid = attr->uid; } if (SETATTR_WANTS_GID(attr)) { gid = attr->gid; } if ((uid != -1) || (gid != -1)) { res = lchown(path, uid, gid); if (res == -1) { return -errno; } } if (SETATTR_WANTS_SIZE(attr)) { res = truncate(path, attr->size); if (res == -1) { return -errno; } } if (SETATTR_WANTS_MODTIME(attr)) { struct timeval tv[2]; if (!SETATTR_WANTS_ACCTIME(attr)) { gettimeofday(&tv[0], NULL); } else { tv[0].tv_sec = attr->acctime.tv_sec; tv[0].tv_usec = attr->acctime.tv_nsec / 1000; } tv[1].tv_sec = attr->modtime.tv_sec; tv[1].tv_usec = attr->modtime.tv_nsec / 1000; res = utimes(path, tv); if (res == -1) { return -errno; } } if (SETATTR_WANTS_CRTIME(attr)) { struct attrlist attributes; attributes.bitmapcount = ATTR_BIT_MAP_COUNT; attributes.reserved = 0; attributes.commonattr = ATTR_CMN_CRTIME; attributes.dirattr = 0; attributes.fileattr = 0; attributes.forkattr = 0; attributes.volattr = 0; res = setattrlist(path, &attributes, &attr->crtime, sizeof(struct timespec), FSOPT_NOFOLLOW); if (res == -1) { return -errno; } } if (SETATTR_WANTS_CHGTIME(attr)) { struct attrlist attributes; attributes.bitmapcount = ATTR_BIT_MAP_COUNT; attributes.reserved = 0; attributes.commonattr = ATTR_CMN_CHGTIME; attributes.dirattr = 0; attributes.fileattr = 0; attributes.forkattr = 0; attributes.volattr = 0; res = setattrlist(path, &attributes, &attr->chgtime, sizeof(struct timespec), FSOPT_NOFOLLOW); if (res == -1) { return -errno; } } if (SETATTR_WANTS_BKUPTIME(attr)) { struct attrlist attributes; attributes.bitmapcount = ATTR_BIT_MAP_COUNT; attributes.reserved = 0; attributes.commonattr = ATTR_CMN_BKUPTIME; attributes.dirattr = 0; attributes.fileattr = 0; attributes.forkattr = 0; attributes.volattr = 0; res = setattrlist(path, &attributes, &attr->bkuptime, sizeof(struct timespec), FSOPT_NOFOLLOW); if (res == -1) { return -errno; } } if (SETATTR_WANTS_FLAGS(attr)) { res = lchflags(path, attr->flags); if (res == -1) { return -errno; } } return 0; } // Note: This is pretty much the same as the fsclient example implementation. // We need to do the same thing, and, there are a lot of macros in use that // are not really documented anywhere. static int fsclient_fsetattr_x(const char *path, struct setattr_x *attr, struct fuse_file_info *fi) { cout << "fsclient_fsetattr_x " << path << endl; int res; uid_t uid = -1; gid_t gid = -1; if (SETATTR_WANTS_MODE(attr)) { cout << "\t" << "fchmod[" << attr->mode << "]" << endl; res = fchmod(fi->fh, attr->mode); if (res == -1) { return -errno; } } if (SETATTR_WANTS_UID(attr)) { uid = attr->uid; } if (SETATTR_WANTS_GID(attr)) { gid = attr->gid; } if ((uid != -1) || (gid != -1)) { res = fchown(fi->fh, uid, gid); if (res == -1) { return -errno; } } if (SETATTR_WANTS_SIZE(attr)) { res = ftruncate(fi->fh, attr->size); if (res == -1) { return -errno; } } if (SETATTR_WANTS_MODTIME(attr)) { struct timeval tv[2]; if (!SETATTR_WANTS_ACCTIME(attr)) { gettimeofday(&tv[0], NULL); } else { tv[0].tv_sec = attr->acctime.tv_sec; tv[0].tv_usec = attr->acctime.tv_nsec / 1000; } tv[1].tv_sec = attr->modtime.tv_sec; tv[1].tv_usec = attr->modtime.tv_nsec / 1000; res = futimes(fi->fh, tv); if (res == -1) { return -errno; } } if (SETATTR_WANTS_CRTIME(attr)) { struct attrlist attributes; attributes.bitmapcount = ATTR_BIT_MAP_COUNT; attributes.reserved = 0; attributes.commonattr = ATTR_CMN_CRTIME; attributes.dirattr = 0; attributes.fileattr = 0; attributes.forkattr = 0; attributes.volattr = 0; res = fsetattrlist(fi->fh, &attributes, &attr->crtime, sizeof(struct timespec), FSOPT_NOFOLLOW); if (res == -1) { return -errno; } } if (SETATTR_WANTS_CHGTIME(attr)) { struct attrlist attributes; attributes.bitmapcount = ATTR_BIT_MAP_COUNT; attributes.reserved = 0; attributes.commonattr = ATTR_CMN_CHGTIME; attributes.dirattr = 0; attributes.fileattr = 0; attributes.forkattr = 0; attributes.volattr = 0; res = fsetattrlist(fi->fh, &attributes, &attr->chgtime, sizeof(struct timespec), FSOPT_NOFOLLOW); if (res == -1) { return -errno; } } if (SETATTR_WANTS_BKUPTIME(attr)) { struct attrlist attributes; attributes.bitmapcount = ATTR_BIT_MAP_COUNT; attributes.reserved = 0; attributes.commonattr = ATTR_CMN_BKUPTIME; attributes.dirattr = 0; attributes.fileattr = 0; attributes.forkattr = 0; attributes.volattr = 0; res = fsetattrlist(fi->fh, &attributes, &attr->bkuptime, sizeof(struct timespec), FSOPT_NOFOLLOW); if (res == -1) { return -errno; } } if (SETATTR_WANTS_FLAGS(attr)) { res = fchflags(fi->fh, attr->flags); if (res == -1) { return -errno; } } return 0; } static int fsclient_statfs(const char *path, struct statvfs *stbuf) { cout << "fsclient_statfs " << path << endl; int res; res = statvfs(ws.path(path).c_str(), stbuf); if (res == -1) { return -errno; } return 0; } static int fsclient_fsync(const char *path, int isdatasync, struct fuse_file_info *fi) { cout << "fsclient_fsync " << path << endl; int res; (void) path; (void) isdatasync; res = fsync(fi->fh); if (res == -1) { return -errno; } return 0; } static int fsclient_setxattr(const char *path, const char *name, const char *value, size_t size, int flags, uint32_t position) { cout << "fsclient_setxattr " << path << endl; int res; if (!strncmp(name, XATTR_APPLE_PREFIX, sizeof(XATTR_APPLE_PREFIX) - 1)) { flags &= ~(XATTR_NOSECURITY); } if (!strcmp(name, A_KAUTH_FILESEC_XATTR)) { char new_name[MAXPATHLEN]; memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR)); memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1); res = setxattr(ws.path(path).c_str(), new_name, value, size, position, XATTR_NOFOLLOW); } else { res = setxattr(ws.path(path).c_str(), name, value, size, position, XATTR_NOFOLLOW); } if (res == -1) { return -errno; } return 0; } static int fsclient_getxattr(const char *path, const char *name, char *value, size_t size, uint32_t position) { cout << "fsclient_getxattr " << path << endl; int res; if (strcmp(name, A_KAUTH_FILESEC_XATTR) == 0) { char new_name[MAXPATHLEN]; memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR)); memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1); res = getxattr(ws.path(path).c_str(), new_name, value, size, position, XATTR_NOFOLLOW); } else { res = getxattr(ws.path(path).c_str(), name, value, size, position, XATTR_NOFOLLOW); } if (res == -1) { return -errno; } return res; } static int fsclient_listxattr(const char *path, char *list, size_t size) { cout << "fsclient_listxattr " << path << endl; ssize_t res = listxattr(ws.path(path).c_str(), list, size, XATTR_NOFOLLOW); if (res > 0) { if (list) { size_t len = 0; char *curr = list; do { size_t thislen = strlen(curr) + 1; if (strcmp(curr, G_KAUTH_FILESEC_XATTR) == 0) { memmove(curr, curr + thislen, res - len - thislen); res -= thislen; break; } curr += thislen; len += thislen; } while (len < res); } else { /* ssize_t res2 = getxattr(path, G_KAUTH_FILESEC_XATTR, NULL, 0, 0, XATTR_NOFOLLOW); if (res2 >= 0) { res -= sizeof(G_KAUTH_FILESEC_XATTR); } */ } } if (res == -1) { return -errno; } return res; } static int fsclient_removexattr(const char *path, const char *name) { cout << "fsclient_removexattr " << path << endl; int res; if (strcmp(name, A_KAUTH_FILESEC_XATTR) == 0) { char new_name[MAXPATHLEN]; memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR)); memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1); res = removexattr(path, new_name, XATTR_NOFOLLOW); } else { res = removexattr(path, name, XATTR_NOFOLLOW); } if (res == -1) { return -errno; } return 0; } //--------------------------------------------------------------------------- // Entry point //--------------------------------------------------------------------------- static struct fuse_operations fsclient_operations = { .create = fsclient_create, .flush = fsclient_flush, .fsetattr_x = fsclient_fsetattr_x, .fsync = fsclient_fsync, .getattr = fsclient_getattr, .getxattr = fsclient_getxattr, .listxattr = fsclient_listxattr, .mkdir = fsclient_mkdir, .mknod = fsclient_mknod, .open = fsclient_open, .opendir = fsclient_opendir, .read = fsclient_read, .readdir = fsclient_readdir, .rename = fsclient_rename, .release = fsclient_release, .releasedir = fsclient_releasedir, .removexattr = fsclient_removexattr, .setattr_x = fsclient_setattr_x, .setxattr = fsclient_setxattr, .statfs = fsclient_statfs, .write = fsclient_write, .unlink = fsclient_unlink, }; int main(int argc, char **argv) { string workspace; string mount; bool debug = false; try { TCLAP::CmdLine cmd("./fsclient -w WS_DIR -m MOUNT_DIR", ' ', "0.1.0", true); TCLAP::ValueArg<string> workspaceArg("w", "workspace", "Local p4 workspace directory", true, "", "string", cmd); TCLAP::ValueArg<string> mountArg("m", "mount", "Local directory to mount as filesystem", true, "", "string", cmd); TCLAP::MultiSwitchArg debugArg("d", "debug", "Run in foreground and print debug messages", cmd); cmd.parse(argc, argv); workspace = workspaceArg.getValue(); mount = mountArg.getValue(); debug = debugArg.getValue(); } catch (TCLAP::ArgException & argException) { cerr << argException.error() << " for " << argException.argId() << endl; } cout << "Mirroring workspace: " << workspace << endl; cout << "Mounting at: " << mount << endl; ws.setWorkspace(workspace); int status = setenv("P4CONFIG", (workspace + "/.p4config").c_str(), 1); if (status == -1) { int err = errno; cerr << strerror(err) << endl; return err; } const char *debugArgv[] = {argv[0], "-d", mount.c_str()}; const char *prodArgv[] = {argv[0], mount.c_str()}; const char **fuseArgv; int fuseArgc; if (debug) { fuseArgc = 3; fuseArgv = debugArgv; } else { fuseArgc = 2; fuseArgv = prodArgv; } return fuse_main(fuseArgc, const_cast<char **>(fuseArgv), &fsclient_operations, NULL); }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#2 | 16119 | tjuricek | Rename/move to meet workshop project conventions. | ||
#1 | 16118 | tjuricek |
FSClient initial version: handles add, edit This is a proof-of-concept app that mirrors an existing Perforce workspace to handle running commands like "p4 add" and "p4 edit" automatically when your apps add and write files. See the readme for more information. |