public override void Release(ReadOnlySpan <byte> path, ref FuseFileInfo fi, Guid fileGuid) { path = base.TransformPath(path); if (debug) { Console.WriteLine($"NeoFS::Release({RawDirs.HR(path)})"); } if (FileContexts.TryGetValue(fi.fh, out var context)) { if (context.ExtAssetSha1 != null) // Asset Mode { base.AssetRelease(path, ref fi, fileGuid); } FileContexts.Remove(fi.fh); } if (fi.fh > 0) { LibC.close((int)fi.fh); } fi.fh = 0; }
public override int ReadDir(ReadOnlySpan <byte> path, ulong offset, ReadDirFlags flags, DirectoryContent content, ref FuseFileInfo fi, Guid fileGuid) { if (debug) { Console.WriteLine($"TopLevel::ReadDir({RawDirs.HR(path)},{offset},{flags})"); } try { content.AddEntry("."); content.AddEntry(".."); foreach (var v in Mountpoints) { content.AddEntry(v.Key.AsSpan()); } return(0); } catch (Exception ex) { Console.WriteLine($"TopLevel::ReadDir() - error {ex.Message}"); return(-LibC.EACCES); } }
// We're just handling the root (/) // All the fileopen stuff should be up the mount level public override int OpenDir(ReadOnlySpan <byte> path, ref FuseFileInfo fi, Guid fileGuid) { if (debug) { Console.WriteLine($"TopLevel::ReadDir({RawDirs.HR(path)})"); } return(0); }
public override int ReleaseDir(ReadOnlySpan <byte> path, ref FuseFileInfo fi, Guid fileGuid) { if (debug) { Console.WriteLine($"NeoFS::ReleaseDir({RawDirs.HR(path)})"); } path = base.TransformPath(path); return(0); }
// Metadata read public override int Access(ReadOnlySpan <byte> path, mode_t mode, Guid fileGuid) { path = base.TransformPath(path); if (debug) { Console.WriteLine($"NeoFS::Access({RawDirs.HR(path)},{mode}"); } var res = LibC.access(toBp(path), (int)mode); if (res < 0) { return(-LibC.errno); } return(0); // base.Access(path, mode); }
public override int ReadDir(ReadOnlySpan <byte> path, ulong offset, ReadDirFlags flags, DirectoryContent content, ref FuseFileInfo fi, Guid fileGuid) { if (debug) { Console.WriteLine($"NeoFS::ReadDir({RawDirs.HR(path)},{offset},{flags})"); } path = base.TransformPath(path); // We only have the low level calls, so we need to do opendir/readdir ourself var dirFd = RawDirs.opendir(RawDirs.ToNullTerm(path.ToArray())); if (dirFd == IntPtr.Zero) { return(0); } content.AddEntry("."); // This can eat strings content.AddEntry(".."); while (true) { var dir = RawDirs.readdir_wrap(dirFd, false); // Don't remove the null if (dir == null) { break; } var d = dir.Value; if (debug) { Console.WriteLine($"{RawDirs.HR(path)} -> {RawDirs.HR(d.d_name)}"); } content.AddEntry(d.d_name.AsSpan()); } RawDirs.closedir(dirFd); return(0); }
// Not sure this will get hit. We may need to return / itself public override int GetAttr(ReadOnlySpan <byte> path, ref stat stat, FuseFileInfoRef fiRef, Guid fileGuid) { if (debug) { Console.WriteLine($"TopLevel::GetAttr({RawDirs.HR(path)})"); } if (path.SequenceEqual(Encoding.ASCII.GetBytes("/").AsSpan())) { stat.st_nlink = 1; stat.st_mode = LibC.S_IFDIR | (mode_t)0b111_110_110; // 444 protection for now stat.st_uid = 10010; // aRec.Stat.uid; stat.st_gid = 10010; // aRec.Stat.gid; stat.st_size = 0; var now = DateTimeOffset.Now.ToUnixTimeSeconds(); stat.st_ctim = new timespec { tv_sec = now, tv_nsec = 0 }; stat.st_mtim = new timespec { tv_sec = now, tv_nsec = 0 }; stat.st_atim = new timespec { tv_sec = now, tv_nsec = 0 }; return(0); } return(-LibC.ENOSYS); }
internal void AssetRelease(ReadOnlySpan <byte> path, ref FuseFileInfo fi, Guid fileGuid) { if (FileContexts.TryGetValue(fi.fh, out var context)) { if (context.AssetLink != null) { //Console.WriteLine($"AssetRelease({RawDirs.HR(path)})"); var assetLink = context.AssetLink; assetLink.Release(); // Internally clear buffers and such } else { Console.WriteLine($"AssetLink is null? {RawDirs.HR(path)}"); } context.AssetLink = null; // Let this go out of scope context.ExtAssetSha1 = null; } else { Console.WriteLine($"AssetRelease no context? fh={fi.fh}"); } }
public override int ReadLink(ReadOnlySpan <byte> path, Span <byte> buffer, Guid fileGuid) { path = base.TransformPath(path); if (debug) { Console.WriteLine($"NeoFS::ReadLink({RawDirs.HR(path)}"); } ssize_t retl; fixed(byte *b = buffer) { retl = LibC.readlink(toBp(path), b, buffer.Length); } if (retl < 0) { return(-LibC.errno); } return(0); }
public override int GetAttr(ReadOnlySpan <byte> path, ref stat stat, FuseFileInfoRef fiRef, Guid fileGuid) { try { path = base.TransformPath(path); if (debug) { Console.WriteLine($"NeoFS::GetAttr({RawDirs.HR(path)})"); fixed(stat *st = &stat) { var lc = LibC.lstat(toBp(path), st); if (lc < 0) { // Windows file explorer hits lot of files that don't exist //Console.WriteLine($"Stat error: {lc} errno={LibC.errno} path={RawDirs.HR(path)}"); return(-LibC.errno); } } // Intercept an asset - we must provide the asset's Attr, not the link of the baked file if ((stat.st_mode & LibC.S_IFMT) == LibC.S_IFLNK) { if (debug) { Console.WriteLine($"Link Seen: Stat mode={stat.st_mode}, S_IFMT={LibC.S_IFMT}, link={LibC.S_IFLNK}"); } ssize_t retl; var buffer = new byte[1024]; fixed(byte *b = buffer) { retl = LibC.readlink(toBp(path), b, buffer.Length); } // Trying to do something like startswith for bytes that's fast var link = MemoryExtensions.AsSpan(buffer, 0, assetTag.Length); if (debug) { Console.WriteLine($"NeoFS::GetAttr Asset Detected - ({RawDirs.HR(path)}, {RawDirs.HR(link)}"); } if (link.SequenceEqual(assetTag)) { link = MemoryExtensions.AsSpan(buffer, 0, (int)retl); if (debug) { Console.WriteLine($"Found ASSET {RawDirs.HR(link)}"); } base.GetAssetAttr(path, link, ref stat, fileGuid); return(0); } } if (debug) { Console.WriteLine($"Stat Dump: size={stat.st_size}, mode={stat.st_mode}, mtim={stat.st_mtim}"); } return(0); } catch (Exception ex) { Console.WriteLine($"GetAttr error: {ex.Message} {ex.StackTrace}"); return(-LibC.ENOENT); } }
// File read public override int Open(ReadOnlySpan <byte> path, ref FuseFileInfo fi, Guid fileGuid) { path = base.TransformPath(path); if (path.Length < 1) { return(-LibC.ENOENT); } if (debug) { Console.WriteLine($"NeoFS::Open()"); } // Stat the file var stat = new stat(); var lc = LibC.lstat(toBp(path), &stat); if (lc < 0) { return(-LibC.errno); } string extAssetSha1 = null; // Intercept an asset - we must provide the asset's Attr, not the link of the baked file if ((stat.st_mode & LibC.S_IFMT) == LibC.S_IFLNK) { if (debug) { Console.WriteLine($"Stat mode={stat.st_mode}, S_IFMT={LibC.S_IFMT}, link={LibC.S_IFLNK}"); } // Get the link ssize_t retl; var buffer = new byte[1024]; fixed(byte *b = buffer) { retl = LibC.readlink(toBp(path), b, buffer.Length); } // Trying to do something like startswith for bytes that's fast var link = MemoryExtensions.AsSpan <byte>(buffer, 0, assetTag.Length); if (debug) { Console.WriteLine($"NeoFS::GetAttr Asset Detected - ({RawDirs.HR(path)}, {RawDirs.HR(link)}"); } if (link.SequenceEqual(assetTag)) { link = MemoryExtensions.AsSpan <byte>(buffer, 0, (int)retl); if (debug) { Console.WriteLine($"Found ASSET {RawDirs.HR(link)}"); } var asset = link.Slice(assetTag.Length); extAssetSha1 = Encoding.ASCII.GetString(asset); } else { link = MemoryExtensions.AsSpan <byte>(buffer, 0, (int)retl); //path = base.TransformPath() - have to figure out how to pass to our mountmapper // Probably a dependancy injection // These links shouldn't happen, but it would be a nice attack vector } } // See if asset -- if so, set to asset mode and call base if (extAssetSha1 != null) // Asset Mode { var fakeFd = LibC.open(toBp(fakeFile), 0); if (fakeFd > 0) { fi.fh = (ulong)fakeFd; FileContexts.Add(fi.fh, new FileContext { AssetLink = null, ExtAssetSha1 = extAssetSha1, ExtFileHandle = null, }); //Console.WriteLine($"Create Context (sha1) ID={fi.fh}"); } else { Console.WriteLine($"Error Opening /dev/null? Error={LibC.errno}"); } return(base.AssetOpen(path, ref fi, fileGuid)); } // transform the link var newFd = LibC.open(toBp(path), fi.flags); if (newFd > 0) { fi.fh = (ulong)newFd; // Make a context anyway FileContexts.Add(fi.fh, new FileContext { AssetLink = null, ExtAssetSha1 = null, ExtFileHandle = null, }); //Console.WriteLine($"Create Context (null) ID={fi.fh}"); return(0); } return(-LibC.errno); }
internal void GetAssetAttr(ReadOnlySpan <byte> path, Span <byte> link, ref stat stat, Guid fileuuid) { // Even though we have the id of the asset, we actually need the entry from BakedFiles, // which should have the last stat on it - or we use FileInfo in the sql database - maybe we'll do both // The asset might exist as lots of different files with lots of date (though only one size) if (debug) { Console.WriteLine($"Found Tag: {RawDirs.HR(path)}={RawDirs.HR(link)}"); } // Had to find/build a python compatible uuid5 -- the guidex module does not make valid uuid5s //var fileuuid = Guid.Empty; //if (fiRef.ExtFileHandle.HasValue) // fileuuid = fiRef.ExtFileHandle.Value; if (assetFiles == null) { assetFiles = NeoMongo.NeoDb.AssetFiles(); } AssetFiles aRec = null; try { var volFilter = Builders <AssetFiles> .Filter.Eq("_id", fileuuid.ToString().ToLower()); aRec = assetFiles.FindSync(volFilter).FirstOrDefault(); } catch (Exception ex) { Console.WriteLine($"Error: ${ex.Message}"); } if (aRec != null) { // These should be synthesized - eventually we'll have more security here - for now // all owned by neo:neo and world readable //stat.st_mode = (mode_t) aRec.Stat.mode; //stat.st_mode = S_IFREG | 0b100_100_100; // r--r--r-- stat.st_nlink = 1; stat.st_mode = LibC.S_IFREG | (mode_t)0b100_100_100; // 444 protection for now stat.st_uid = 10010; // aRec.Stat.uid; stat.st_gid = 10010; // aRec.Stat.gid; if (aRec.Stat != null) { stat.st_size = aRec.Stat.size; stat.st_ctim = new timespec { tv_sec = aRec.Stat.ctime, tv_nsec = 0 }; stat.st_mtim = new timespec { tv_sec = aRec.Stat.mtime, tv_nsec = 0 }; stat.st_atim = new timespec { tv_sec = aRec.Stat.atime, tv_nsec = 0 }; } else { // Just turn the link into our stat, we'll just keep the dates and make it into a file stat.st_nlink = 1; stat.st_mode = LibC.S_IFREG | (mode_t)0b100_100_100; // 444 protection for now stat.st_uid = 10010; // aRec.Stat.uid; stat.st_gid = 10010; // aRec.Stat.gid; Console.WriteLine($"No internal Stat: {RawDirs.HR(path)}={RawDirs.HR(link)}"); } if (debug) { Console.WriteLine($"Stat Asset Dump: size={stat.st_size}, mode={stat.st_mode}, mtim={stat.st_mtim}"); } if (debug) { Console.WriteLine($"Return stat for: {fileuuid.ToString().ToLower()}"); } return; } Console.WriteLine($"Can't find AssetFile: {fileuuid.ToString().ToLower()}"); }