Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support storing UnixFS 1.5 Mode and ModTime #10478

Merged
merged 30 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
095c18e
preliminary support for unixfs Mode and ModTime
gammazero Aug 13, 2024
60f3470
mod tidy examples
gammazero Aug 13, 2024
beae0b3
lint fix
gammazero Aug 13, 2024
633f28f
Update changelog
gammazero Aug 13, 2024
31b8024
test(mode): expect empty octal as well
lidel Aug 13, 2024
b170c90
chore: latest commit
lidel Aug 13, 2024
8e0308b
docs(changelong): mode/mtime cli example
lidel Aug 13, 2024
4817ac7
fix(rpc): cli error instead of daemon println
lidel Aug 13, 2024
f0fea76
chore(files): explicit 'not set' in missing stats
lidel Aug 13, 2024
0c55944
test(ci): enable running t0047-add-mode-mtime.sh
lidel Aug 13, 2024
988597a
Fix RPC decoding
gammazero Aug 14, 2024
8d75eb2
Use latest boxo PR version
gammazero Aug 14, 2024
97e10db
Merge branch 'master' into feat/unixfs1.5-mode-mtime
lidel Aug 14, 2024
b47e56a
chore(ci): verbose log of mod tests
lidel Aug 14, 2024
60fd454
chore(ci): log ci filesystem attributes
lidel Aug 14, 2024
2e8afc0
chore(ci): inspect stat of unecpected permissions
lidel Aug 14, 2024
0312a99
Implement symlink support in client/rpc
gammazero Aug 14, 2024
0987496
refactor(test): isolate outputs from fixtures
lidel Aug 14, 2024
5d23bcb
fix(ci): force umask 022
lidel Aug 14, 2024
d6c6999
Made mode and mod time work for items within a directory
gammazero Aug 16, 2024
7ac1c81
Explicitly disable raw leaves if preserving mode or modification time
gammazero Aug 19, 2024
b549434
Merge branch 'master' into feat/unixfs1.5-mode-mtime
gammazero Aug 19, 2024
577677c
docs: note raw-leaves impact, mark experimental
lidel Aug 19, 2024
01e6823
refactor: error in user provided raw-leaves=true
lidel Aug 19, 2024
1c3cc4c
chore: hardcode import defaults used for tests
lidel Aug 20, 2024
32c6a1c
test: mode/mtime roundtrip with cidv1
lidel Aug 20, 2024
55dad60
test: files chmod|touch with cidv1
lidel Aug 20, 2024
826aae1
update boxo
gammazero Aug 20, 2024
2235051
Update boxo to latest unixfs PR version
gammazero Aug 20, 2024
9043c55
chore: latest boxo main
lidel Aug 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading