/* * Minimal puffs filesystem. It mounts itself as min-puffs on /mnt, * ro,sync,noexec,nocoredump,symperm. It presents itself as a * directory containing a file called, rather unimaginatively, "file"; * this file contains the six characters "hello\n". */ #include #include #include #include #include typedef struct puffs_pathobj PUFFS_PATHOBJ; typedef struct puffs_pathinfo PUFFS_PATHINFO; typedef struct puffs_kcache PUFFS_KCACHE; typedef struct puffs_node PUFFS_NODE; typedef struct puffs_usermount PUFFS_USERMOUNT; typedef struct puffs_cn PUFFS_CN; typedef struct puffs_ops PUFFS_OPS; typedef struct puffs_cred PUFFS_CRED; typedef struct puffs_newinfo PUFFS_NEWINFO; typedef struct statvfs STATVFS; typedef struct vattr VATTR; typedef struct dirent DIRENT; // These are documented but not actually defined(!) // These seem to be the actual values used. #define PUFFSLOOKUP_LOOKUP 0 #define PUFFSLOOKUP_CREATE 1 #define PUFFSLOOKUP_DELETE 2 #define PUFFSLOOKUP_RENAME 3 static PUFFS_USERMOUNT *um; static char cookie_root; static char cookie_file; static PUFFS_NODE *pn_root; static PUFFS_NODE *pn_file; static void verb(const char *, ...) __attribute__((__format__(__printf__,1,2))); static void verb(const char *fmt, ...) { va_list ap; char *s; int l; va_start(ap,fmt); l = vasprintf(&s,fmt,ap); va_end(ap); printf("%s\n",s); free(s); } static const char *cookie_str(const puffs_cookie_t c) { static char rbuf[64]; if (c == &cookie_root) return(""); if (c == &cookie_file) return(""); sprintf(&rbuf[0],"%p",(const void *)c); return(&rbuf[0]); } static const char *access_mode_str(int m) { static char rbuf[64]; switch (m) { case PUFFS_VREAD: return("READ"); break; case PUFFS_VWRITE: return("WRITE"); break; case PUFFS_VEXEC: return("EXEC"); break; } sprintf(&rbuf[0],"?%d",m); return(&rbuf[0]); } static const char *lookup_nameiop_str(uint32_t op) { static char rbuf[64]; switch (op) { case PUFFSLOOKUP_LOOKUP: return("LOOKUP"); break; case PUFFSLOOKUP_CREATE: return("CREATE"); break; case PUFFSLOOKUP_DELETE: return("DELETE"); break; case PUFFSLOOKUP_RENAME: return("RENAME"); break; } sprintf(&rbuf[0],"?%lld",(unsigned long long int)op); return(&rbuf[0]); } static int op_fs_unmount(PUFFS_USERMOUNT *um, int flags) { verb("%s, flags %#x",__func__,(unsigned int)flags); (void)um; (void)flags; return(0); } static int op_fs_statvfs(PUFFS_USERMOUNT *um, STATVFS *s) { verb("%s",__func__); (void)um; s->f_bsize = 512; s->f_frsize = 512; s->f_blocks = 0; s->f_bfree = 0; s->f_bavail = 0; s->f_bresvd = 0; s->f_files = 2; s->f_ffree = 0; s->f_favail = 0; s->f_fresvd = 0; return(0); } static int op_fs_sync(PUFFS_USERMOUNT *um, int waitfor, const PUFFS_CRED *cr) { const char *wfs; switch (waitfor) { case MNT_WAIT: wfs = "WAIT"; break; case MNT_NOWAIT: wfs = "NOWAIT"; break; case MNT_LAZY: wfs = "LAZY"; break; default: { static char buf[64]; sprintf(&buf[0],"?%d",waitfor); wfs = &buf[0]; } break; } verb("%s: waitfor %s, cr %p",__func__,wfs,(const void *)cr); (void)um; (void)waitfor; (void)cr; return(0); } static int op_fs_fhtonode(PUFFS_USERMOUNT *um, void *fh, size_t fhsize, PUFFS_NEWINFO *ni) { verb("%s",__func__); (void)um; (void)fh; (void)fhsize; (void)ni; return(EOPNOTSUPP); } static int op_fs_nodetofh(PUFFS_USERMOUNT *um, puffs_cookie_t cook, void *fh, size_t *fhsize) { verb("%s",__func__); (void)um; (void)cook; (void)fh; (void)fhsize; return(EOPNOTSUPP); } static void op_fs_suspend(PUFFS_USERMOUNT *um, int status) { const char *ss; switch (status) { case PUFFS_SUSPEND_START: ss = "START"; break; case PUFFS_SUSPEND_SUSPENDED: ss = "SUSPENDED"; break; case PUFFS_SUSPEND_RESUME: ss = "RESUME"; break; case PUFFS_SUSPEND_ERROR: ss = "ERROR"; break; default: { static char buf[64]; sprintf(&buf[0],"?%d",status); ss = &buf[0]; } break; } verb("%s: waitfor %s",__func__,ss); (void)um; (void)status; } static int op_fs_extattrctl(PUFFS_USERMOUNT *um, int a, puffs_cookie_t b, int c, int d, const char *e) { verb("%s",__func__); (void)um; (void)a; (void)b; (void)c; (void)d; (void)e; return(EOPNOTSUPP); } static int op_node_getattr(PUFFS_USERMOUNT *um, puffs_cookie_t cook, VATTR *va, const PUFFS_CRED *cr) { struct timeval tv; verb("%s cookie %s va %p cr %p",__func__,cookie_str(cook),(void *)va, (const void *)cr); (void)um; gettimeofday(&tv,0); if (cook == &cookie_root) { va->va_type = VDIR; va->va_mode = S_IFDIR | 0755; va->va_nlink = 1; va->va_uid = 0; va->va_gid = 0; va->va_fileid = 1; va->va_size = 512; va->va_blocksize = 512; va->va_atime.tv_sec = tv.tv_sec; va->va_atime.tv_nsec = tv.tv_usec * 1000; va->va_mtime = va->va_atime; va->va_ctime = va->va_atime; va->va_bytes = 512; } else if (cook == &cookie_file) { va->va_type = VREG; va->va_mode = S_IFREG | 0644; va->va_nlink = 1; va->va_uid = 0; va->va_gid = 0; va->va_fileid = 2; va->va_size = 6; va->va_blocksize = 512; va->va_atime.tv_sec = tv.tv_sec; va->va_atime.tv_nsec = tv.tv_usec * 1000; va->va_mtime = va->va_atime; va->va_ctime = va->va_atime; va->va_bytes = 512; } else { return(EOPNOTSUPP); } va->va_fsid = 0; va->va_birthtime.tv_sec = 0; va->va_birthtime.tv_nsec = 0; va->va_gen = 0; va->va_flags = 0; va->va_rdev = 0; va->va_filerev = 0; va->va_vaflags = 0; va->va_spare = 0; return(0); } static int op_node_inactive(PUFFS_USERMOUNT *um, puffs_cookie_t cook) { verb("%s cookie %s",__func__,cookie_str(cook)); (void)um; (void)cook; return(0); } static int op_node_access(PUFFS_USERMOUNT *um, puffs_cookie_t cook, int mode, const PUFFS_CRED *cr) { verb("%s cookie %s mode %s cred %p",__func__,cookie_str(cook),access_mode_str(mode),(const void *)cr); (void)um; (void)cook; (void)mode; (void)cr; switch (mode) { case PUFFS_VREAD: return(0); break; case PUFFS_VWRITE: return(EROFS); break; case PUFFS_VEXEC: return(0); break; } return(EINVAL); } static int op_node_open(PUFFS_USERMOUNT *um, puffs_cookie_t cook, int mode, const PUFFS_CRED *cr) { verb("%s cookie %s mode %#x cr %p",__func__,cookie_str(cook),(unsigned int)mode,(const void *)cr); (void)um; (void)cook; (void)mode; (void)cr; if (mode & FWRITE) return(EROFS); return(0); } static int op_node_seek(PUFFS_USERMOUNT *um, puffs_cookie_t cook, off_t old, off_t new, const PUFFS_CRED *cr) { verb("%s cookie %s old %#llx new %#llx cr %p",__func__,cookie_str(cook),(unsigned long long int)old,(unsigned long long int)new,(const void *)cr); (void)um; (void)cook; (void)old; (void)new; (void)cr; return(0); } static int op_node_readdir( PUFFS_USERMOUNT *um, puffs_cookie_t cook, DIRENT *dent, off_t *off, size_t *len, const PUFFS_CRED *cr, int *eofp, off_t *cookies, size_t *ncookies ) { off_t o; verb("%s cookie %s ents %p off %p (%lld) spc %p (%lld) cr %p eofp %p (%d)",__func__,cookie_str(cook),(void *)dent,(void *)off,(unsigned long long int)*off,(void *)len,(unsigned long long int)*len,(const void *)cr,(void *)eofp,*eofp); (void)um; (void)cookies; (void)ncookies; if (cook == &cookie_root) { } else if (cook == &cookie_file) { return(ENOTDIR); } else { return(EINVAL); } o = *off; switch (o) { case 0: if (! puffs_nextdent(&dent,".",1,DT_DIR,len)) break; o = 1; /* fall through */ case 1: if (! puffs_nextdent(&dent,"..",1,DT_DIR,len)) break; o = 2; /* fall through */ case 2: if (! puffs_nextdent(&dent,"file",2,DT_REG,len)) break; o = 3; /* fall through */ default: *eofp = 1; break; } *off = o; return(0); } static int op_node_close(PUFFS_USERMOUNT *um, puffs_cookie_t cook, int oflags, const PUFFS_CRED *cr) { verb("%s cookie %s oflags %#x cr %p",__func__,cookie_str(cook),(unsigned int)oflags,(const void *)cr); (void)um; (void)cook; (void)oflags; (void)cr; return(0); } static int op_node_lookup(PUFFS_USERMOUNT *um, puffs_cookie_t cook, PUFFS_NEWINFO *ni, const PUFFS_CN *cn) { verb("%s lookup %s ni %p cn %p [op %s flags %x name %s len %llu consume %llu]", __func__, cookie_str(cook), (void *)ni, (const void *)cn, lookup_nameiop_str(cn->pcn_pkcnp->pkcn_nameiop), (unsigned int)cn->pcn_pkcnp->pkcn_flags, &cn->pcn_pkcnp->pkcn_name[0], (unsigned long long int)cn->pcn_pkcnp->pkcn_namelen, (unsigned long long int)cn->pcn_pkcnp->pkcn_consume); if (cook == &cookie_root) { } else if (cook == &cookie_file) { return(ENOTDIR); } else { return(EINVAL); } switch (cn->pcn_pkcnp->pkcn_nameiop) { case PUFFSLOOKUP_LOOKUP: break; case PUFFSLOOKUP_CREATE: case PUFFSLOOKUP_DELETE: case PUFFSLOOKUP_RENAME: return(EROFS); break; } if ( (cn->pcn_pkcnp->pkcn_namelen == 1) && !bcmp(&cn->pcn_pkcnp->pkcn_name[0],".",1) ) { puffs_newinfo_setcookie(ni,&cookie_root); puffs_newinfo_setvtype(ni,VDIR); } else if ( (cn->pcn_pkcnp->pkcn_namelen == 4) && !bcmp(&cn->pcn_pkcnp->pkcn_name[0],"file",4) ) { puffs_newinfo_setcookie(ni,&cookie_file); puffs_newinfo_setvtype(ni,VREG); puffs_newinfo_setsize(ni,6); } else { return(ENOENT); } // puffs_newinfo_setrdev for device special files (void)um; (void)cook; (void)ni; (void)cn; return(0); } static int op_node_read(PUFFS_USERMOUNT *um, puffs_cookie_t cook, uint8_t *buf, off_t off, size_t *resid, const PUFFS_CRED *cr, int ioflag) { int o; int r; verb("%s read %s buf %p off %lld resid %p (%llu) cr %p ioflag %d", __func__, cookie_str(cook), (void *)buf, (long long int)off, (void *)resid, (unsigned long long int)*resid, (const void *)cr, ioflag); if (cook == &cookie_root) { return(EISDIR); } else if (cook == &cookie_file) { } else { return(EINVAL); } if (off < 0) return(EINVAL); if (off >= 6) return(0); o = off; r = (*resid > 6) ? 6 : *resid; if (o+r > 6) r = 6 - o; bcopy("hello\n"+o,buf,r); *resid -= r; (void)um; (void)cook; (void)buf; (void)off; (void)resid; (void)cr; (void)ioflag; return(0); } static int op_node_fsync(PUFFS_USERMOUNT *um, puffs_cookie_t cook, const PUFFS_CRED *cr, int flags, off_t offlo, off_t offhi) { verb("%s cookie %s cr %p flags %d off lo %lld hi %lld",__func__,cookie_str(cook),(const void *)cr,flags,(long long int)offlo,(long long int)offhi); (void)um; (void)cook; (void)cr; (void)flags; (void)offlo; (void)offhi; return(0); } static int op_node_reclaim(PUFFS_USERMOUNT *um, puffs_cookie_t cook) { verb("%s cookie %s",__func__,cookie_str(cook)); (void)um; (void)cook; return(0); } static PUFFS_OPS ops = { .puffs_fs_unmount = &op_fs_unmount, .puffs_fs_statvfs = &op_fs_statvfs, .puffs_fs_sync = &op_fs_sync, .puffs_fs_fhtonode = &op_fs_fhtonode, .puffs_fs_nodetofh = &op_fs_nodetofh, .puffs_fs_suspend = &op_fs_suspend, .puffs_fs_extattrctl = &op_fs_extattrctl, .puffs_node_lookup = &op_node_lookup, // create // mknod .puffs_node_open = &op_node_open, .puffs_node_close = &op_node_close, .puffs_node_access = &op_node_access, .puffs_node_getattr = &op_node_getattr, // setattr // poll // mmap .puffs_node_fsync = &op_node_fsync, .puffs_node_seek = &op_node_seek, // remove // link // rename // mkdir // rmdir // symlink .puffs_node_readdir = &op_node_readdir, // readlink .puffs_node_reclaim = &op_node_reclaim, .puffs_node_inactive = &op_node_inactive, // print // pathconf // advlock .puffs_node_read = &op_node_read, // write // abortop // getextattr // setextattr // listextattr // deleteextattr }; int main(void); int main(void) { int e; // puffs_init frees the ops struct(!); this is undocumented(!!). { PUFFS_OPS *op; op = malloc(sizeof(PUFFS_OPS)); *op = ops; um = puffs_init(op,"min-puffs","minimal",0,PUFFS_KFLAG_NOCACHE|PUFFS_KFLAG_ALLOPS |PUFFS_FLAG_OPDUMP ); } if (! um) { fprintf(stderr,"puffs_init failed\n"); exit(1); } pn_root = puffs_pn_new(um,&cookie_root); if (! pn_root) { fprintf(stderr,"can't create root node\n"); exit(1); } pn_file = puffs_pn_new(um,&cookie_file); if (! pn_file) { fprintf(stderr,"can't create file node\n"); exit(1); } // puffs_setncookiehash if appropriate e = puffs_mount(um,"/mnt",MNT_RDONLY|MNT_NOEXEC|MNT_NOCOREDUMP|MNT_SYMPERM|MNT_SYNCHRONOUS,&cookie_root); if (e) { printf("puffs_mount returned %d\n",e); exit(1); } printf("root: node %p cookie %p\n",(void *)pn_root,(void *)&cookie_root); printf("file: node %p cookie %p\n",(void *)pn_file,(void *)&cookie_file); // puffs_getselectable(um) // puffs_setblockingmode(um,xxx) // puffs_setroot if puffs_path framework is used // puffs_sethfsize if NFS-exportable e = puffs_mainloop(um); // NOTE framework assumes cookies are struct puffs_node pointers(!!) //see puffs_node(3) printf("puffs_mainloop returned %d\n",e); return(0); }