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);
            }
        }
        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;
        }
        // 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);
 }
        public unsafe override int GetAttr(ReadOnlySpan <byte> path, ref stat stat, FuseFileInfoRef fiRef)
        {
            try
            {
                if (verbosity > 10)
                {
                    Console.WriteLine($"GetAttr {path.GetString()}");
                }

                int error = 0, level = 0;
                var procs = ProcPath(path, ref error, ref level, mustExist: true);
                if (error != 0)
                {
                    return(-LibC.ENOENT);
                }

                var last = procs.Pop();
                //Console.WriteLine($"   getAttr - last {last.Item1.GetString()} error {last.Item3}");

                if (last.Item2 == null)
                {
                    return(-last.Item3);
                }

                //Console.WriteLine($" return: {last.Item2.Stat.st_mode.ModMask()}");

                last.Item2.GetStat(ref stat);
                var physFile = last.Item2.GetStatPhysical();
                if (physFile != null)
                {
                    var nstat = new stat();

                    var lc = LibC.lstat(RawDirs.ToBytePtr(physFile.ToArray()), &nstat);
                    if (lc < 0)
                    {
                        return(-LibC.errno);
                    }

                    // Real stat is kept in a physical backing file (mostly st_size)
                    // Not trying to keep this syned in the database yet
                    last.Item2.GetStat(ref stat, nstat);
                }
                //Console.WriteLine($"Length Stat={stat.st_size}");

                return(0);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error GetAttr: {ex.Message} {ex.StackTrace}");
                return(-LibC.EIO);
            }
        }
        // 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);
        }
Exemple #7
0
        public static List <ReadOnlyMemory <byte> > Scan(ReadOnlySpan <byte> path)
        {
            //Console.WriteLine($"Scan: {path.GetString()}");
            var dirFd = RawDirs.opendir(RawDirs.ToNullTerm(path.ToArray()));

            if (dirFd == IntPtr.Zero)
            {
                throw new Exception($"Error opening directory {path.GetString()} - {LibC.errno}");
            }

            var contents = new List <ReadOnlyMemory <byte> >();

            Console.Write($"\u000d-------- [Scanning: {path.GetString()}]\u001b[K\u001b[J\u000d");

            int ct = 0;

            while (true)
            {
                var dir = RawDirs.readdir_wrap(dirFd, false);  // Don't remove the null
                if (dir == null)
                {
                    break;
                }

                ct++;

                var d = dir.Value;

                if (ct % 1000 == 0)
                {
                    Console.Write($"\u000d{ct.ToString("00000000")}\u000d");
                }

                //Console.WriteLine($"See {d.d_name.GetString()}");

                contents.Add(d.d_name.AsMemory <byte>());
            }

            Console.Write($"\u000d-------- [Scanned:  {path.GetString()} ({contents.Count} entries)]\u001b[K\u001b[J\u000d");

            RawDirs.closedir(dirFd);

            return(contents);
        }
        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);
        }
Exemple #14
0
 public unsafe new byte *toBp(ReadOnlySpan <byte> path)
 {
     return(RawDirs.ToBytePtr(path.ToArray()));
 }
        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()}");
        }