Skip to content

Commit

Permalink
feat: Support storing UnixFS 1.5 Mode and ModTime (#10478)
Browse files Browse the repository at this point in the history
Co-authored-by: Marcin Rataj <[email protected]>
  • Loading branch information
gammazero and lidel authored Aug 21, 2024
1 parent c5b0428 commit 263edb2
Show file tree
Hide file tree
Showing 21 changed files with 1,105 additions and 91 deletions.
123 changes: 104 additions & 19 deletions client/rpc/apifile.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package rpc

import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"os"
"strconv"
"time"

"github.com/ipfs/boxo/files"
unixfs "github.com/ipfs/boxo/ipld/unixfs"
Expand All @@ -24,20 +28,35 @@ func (api *UnixfsAPI) Get(ctx context.Context, p path.Path) (files.Node, error)
}

var stat struct {
Hash string
Type string
Size int64 // unixfs size
Hash string
Type string
Size int64 // unixfs size
Mode string
Mtime int64
MtimeNsecs int
}
err := api.core().Request("files/stat", p.String()).Exec(ctx, &stat)
if err != nil {
return nil, err
}

mode, err := stringToFileMode(stat.Mode)
if err != nil {
return nil, err
}

var modTime time.Time
if stat.Mtime != 0 {
modTime = time.Unix(stat.Mtime, int64(stat.MtimeNsecs)).UTC()
}

switch stat.Type {
case "file":
return api.getFile(ctx, p, stat.Size)
return api.getFile(ctx, p, stat.Size, mode, modTime)
case "directory":
return api.getDir(ctx, p, stat.Size)
return api.getDir(ctx, p, stat.Size, mode, modTime)
case "symlink":
return api.getSymlink(ctx, p, modTime)
default:
return nil, fmt.Errorf("unsupported file type '%s'", stat.Type)
}
Expand All @@ -49,6 +68,9 @@ type apiFile struct {
size int64
path path.Path

mode os.FileMode
mtime time.Time

r *Response
at int64
}
Expand Down Expand Up @@ -128,16 +150,37 @@ func (f *apiFile) Close() error {
return nil
}

func (f *apiFile) Mode() os.FileMode {
return f.mode
}

func (f *apiFile) ModTime() time.Time {
return f.mtime
}

func (f *apiFile) Size() (int64, error) {
return f.size, nil
}

func (api *UnixfsAPI) getFile(ctx context.Context, p path.Path, size int64) (files.Node, error) {
func stringToFileMode(mode string) (os.FileMode, error) {
if mode == "" {
return 0, nil
}
mode64, err := strconv.ParseUint(mode, 8, 32)
if err != nil {
return 0, fmt.Errorf("cannot parse mode %s: %s", mode, err)
}
return os.FileMode(uint32(mode64)), nil
}

func (api *UnixfsAPI) getFile(ctx context.Context, p path.Path, size int64, mode os.FileMode, mtime time.Time) (files.Node, error) {
f := &apiFile{
ctx: ctx,
core: api.core(),
size: size,
path: p,
ctx: ctx,
core: api.core(),
size: size,
path: p,
mode: mode,
mtime: mtime,
}

return f, f.reset()
Expand Down Expand Up @@ -195,13 +238,19 @@ func (it *apiIter) Next() bool {

switch it.cur.Type {
case unixfs.THAMTShard, unixfs.TMetadata, unixfs.TDirectory:
it.curFile, err = it.core.getDir(it.ctx, path.FromCid(c), int64(it.cur.Size))
it.curFile, err = it.core.getDir(it.ctx, path.FromCid(c), int64(it.cur.Size), it.cur.Mode, it.cur.ModTime)
if err != nil {
it.err = err
return false
}
case unixfs.TFile:
it.curFile, err = it.core.getFile(it.ctx, path.FromCid(c), int64(it.cur.Size))
it.curFile, err = it.core.getFile(it.ctx, path.FromCid(c), int64(it.cur.Size), it.cur.Mode, it.cur.ModTime)
if err != nil {
it.err = err
return false
}
case unixfs.TSymlink:
it.curFile, err = it.core.getSymlink(it.ctx, path.FromCid(c), it.cur.ModTime)
if err != nil {
it.err = err
return false
Expand All @@ -223,13 +272,24 @@ type apiDir struct {
size int64
path path.Path

mode os.FileMode
mtime time.Time

dec *json.Decoder
}

func (d *apiDir) Close() error {
return nil
}

func (d *apiDir) Mode() os.FileMode {
return d.mode
}

func (d *apiDir) ModTime() time.Time {
return d.mtime
}

func (d *apiDir) Size() (int64, error) {
return d.size, nil
}
Expand All @@ -242,7 +302,7 @@ func (d *apiDir) Entries() files.DirIterator {
}
}

func (api *UnixfsAPI) getDir(ctx context.Context, p path.Path, size int64) (files.Node, error) {
func (api *UnixfsAPI) getDir(ctx context.Context, p path.Path, size int64, mode os.FileMode, modTime time.Time) (files.Node, error) {
resp, err := api.core().Request("ls", p.String()).
Option("resolve-size", true).
Option("stream", true).Send(ctx)
Expand All @@ -253,18 +313,43 @@ func (api *UnixfsAPI) getDir(ctx context.Context, p path.Path, size int64) (file
return nil, resp.Error
}

d := &apiDir{
ctx: ctx,
core: api,
size: size,
path: p,
data, _ := io.ReadAll(resp.Output)
rdr := bytes.NewReader(data)

dec: json.NewDecoder(resp.Output),
d := &apiDir{
ctx: ctx,
core: api,
size: size,
path: p,
mode: mode,
mtime: modTime,

//dec: json.NewDecoder(resp.Output),
dec: json.NewDecoder(rdr),
}

return d, nil
}

func (api *UnixfsAPI) getSymlink(ctx context.Context, p path.Path, modTime time.Time) (files.Node, error) {
resp, err := api.core().Request("cat", p.String()).
Option("resolve-size", true).
Option("stream", true).Send(ctx)
if err != nil {
return nil, err
}
if resp.Error != nil {
return nil, resp.Error
}

target, err := io.ReadAll(resp.Output)
if err != nil {
return nil, err
}

return files.NewSymlinkFile(string(target), modTime), nil
}

var (
_ files.File = &apiFile{}
_ files.Directory = &apiDir{}
Expand Down
19 changes: 13 additions & 6 deletions client/rpc/unixfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"errors"
"fmt"
"io"
"os"
"time"

"github.com/ipfs/boxo/files"
unixfs "github.com/ipfs/boxo/ipld/unixfs"
Expand Down Expand Up @@ -80,14 +82,13 @@ func (api *UnixfsAPI) Add(ctx context.Context, f files.Node, opts ...caopts.Unix
}
defer resp.Output.Close()
dec := json.NewDecoder(resp.Output)
loop:

for {
var evt addEvent
switch err := dec.Decode(&evt); err {
case nil:
case io.EOF:
break loop
default:
if err := dec.Decode(&evt); err != nil {
if errors.Is(err, io.EOF) {
break
}
return path.ImmutablePath{}, err
}
out = evt
Expand Down Expand Up @@ -129,6 +130,9 @@ type lsLink struct {
Size uint64
Type unixfs_pb.Data_DataType
Target string

Mode os.FileMode
ModTime time.Time
}

type lsObject struct {
Expand Down Expand Up @@ -222,6 +226,9 @@ func (api *UnixfsAPI) Ls(ctx context.Context, p path.Path, opts ...caopts.Unixfs
Size: l0.Size,
Type: ftype,
Target: l0.Target,

Mode: l0.Mode,
ModTime: l0.ModTime,
}:
case <-ctx.Done():
}
Expand Down
Loading

0 comments on commit 263edb2

Please sign in to comment.