Skip to content

Commit 263edb2

Browse files
gammazerolidel
andauthored
feat: Support storing UnixFS 1.5 Mode and ModTime (#10478)
Co-authored-by: Marcin Rataj <[email protected]>
1 parent c5b0428 commit 263edb2

File tree

21 files changed

+1105
-91
lines changed

21 files changed

+1105
-91
lines changed

client/rpc/apifile.go

+104-19
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package rpc
22

33
import (
4+
"bytes"
45
"context"
56
"encoding/json"
67
"fmt"
78
"io"
9+
"os"
10+
"strconv"
11+
"time"
812

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

2630
var stat struct {
27-
Hash string
28-
Type string
29-
Size int64 // unixfs size
31+
Hash string
32+
Type string
33+
Size int64 // unixfs size
34+
Mode string
35+
Mtime int64
36+
MtimeNsecs int
3037
}
3138
err := api.core().Request("files/stat", p.String()).Exec(ctx, &stat)
3239
if err != nil {
3340
return nil, err
3441
}
3542

43+
mode, err := stringToFileMode(stat.Mode)
44+
if err != nil {
45+
return nil, err
46+
}
47+
48+
var modTime time.Time
49+
if stat.Mtime != 0 {
50+
modTime = time.Unix(stat.Mtime, int64(stat.MtimeNsecs)).UTC()
51+
}
52+
3653
switch stat.Type {
3754
case "file":
38-
return api.getFile(ctx, p, stat.Size)
55+
return api.getFile(ctx, p, stat.Size, mode, modTime)
3956
case "directory":
40-
return api.getDir(ctx, p, stat.Size)
57+
return api.getDir(ctx, p, stat.Size, mode, modTime)
58+
case "symlink":
59+
return api.getSymlink(ctx, p, modTime)
4160
default:
4261
return nil, fmt.Errorf("unsupported file type '%s'", stat.Type)
4362
}
@@ -49,6 +68,9 @@ type apiFile struct {
4968
size int64
5069
path path.Path
5170

71+
mode os.FileMode
72+
mtime time.Time
73+
5274
r *Response
5375
at int64
5476
}
@@ -128,16 +150,37 @@ func (f *apiFile) Close() error {
128150
return nil
129151
}
130152

153+
func (f *apiFile) Mode() os.FileMode {
154+
return f.mode
155+
}
156+
157+
func (f *apiFile) ModTime() time.Time {
158+
return f.mtime
159+
}
160+
131161
func (f *apiFile) Size() (int64, error) {
132162
return f.size, nil
133163
}
134164

135-
func (api *UnixfsAPI) getFile(ctx context.Context, p path.Path, size int64) (files.Node, error) {
165+
func stringToFileMode(mode string) (os.FileMode, error) {
166+
if mode == "" {
167+
return 0, nil
168+
}
169+
mode64, err := strconv.ParseUint(mode, 8, 32)
170+
if err != nil {
171+
return 0, fmt.Errorf("cannot parse mode %s: %s", mode, err)
172+
}
173+
return os.FileMode(uint32(mode64)), nil
174+
}
175+
176+
func (api *UnixfsAPI) getFile(ctx context.Context, p path.Path, size int64, mode os.FileMode, mtime time.Time) (files.Node, error) {
136177
f := &apiFile{
137-
ctx: ctx,
138-
core: api.core(),
139-
size: size,
140-
path: p,
178+
ctx: ctx,
179+
core: api.core(),
180+
size: size,
181+
path: p,
182+
mode: mode,
183+
mtime: mtime,
141184
}
142185

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

196239
switch it.cur.Type {
197240
case unixfs.THAMTShard, unixfs.TMetadata, unixfs.TDirectory:
198-
it.curFile, err = it.core.getDir(it.ctx, path.FromCid(c), int64(it.cur.Size))
241+
it.curFile, err = it.core.getDir(it.ctx, path.FromCid(c), int64(it.cur.Size), it.cur.Mode, it.cur.ModTime)
199242
if err != nil {
200243
it.err = err
201244
return false
202245
}
203246
case unixfs.TFile:
204-
it.curFile, err = it.core.getFile(it.ctx, path.FromCid(c), int64(it.cur.Size))
247+
it.curFile, err = it.core.getFile(it.ctx, path.FromCid(c), int64(it.cur.Size), it.cur.Mode, it.cur.ModTime)
248+
if err != nil {
249+
it.err = err
250+
return false
251+
}
252+
case unixfs.TSymlink:
253+
it.curFile, err = it.core.getSymlink(it.ctx, path.FromCid(c), it.cur.ModTime)
205254
if err != nil {
206255
it.err = err
207256
return false
@@ -223,13 +272,24 @@ type apiDir struct {
223272
size int64
224273
path path.Path
225274

275+
mode os.FileMode
276+
mtime time.Time
277+
226278
dec *json.Decoder
227279
}
228280

229281
func (d *apiDir) Close() error {
230282
return nil
231283
}
232284

285+
func (d *apiDir) Mode() os.FileMode {
286+
return d.mode
287+
}
288+
289+
func (d *apiDir) ModTime() time.Time {
290+
return d.mtime
291+
}
292+
233293
func (d *apiDir) Size() (int64, error) {
234294
return d.size, nil
235295
}
@@ -242,7 +302,7 @@ func (d *apiDir) Entries() files.DirIterator {
242302
}
243303
}
244304

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

256-
d := &apiDir{
257-
ctx: ctx,
258-
core: api,
259-
size: size,
260-
path: p,
316+
data, _ := io.ReadAll(resp.Output)
317+
rdr := bytes.NewReader(data)
261318

262-
dec: json.NewDecoder(resp.Output),
319+
d := &apiDir{
320+
ctx: ctx,
321+
core: api,
322+
size: size,
323+
path: p,
324+
mode: mode,
325+
mtime: modTime,
326+
327+
//dec: json.NewDecoder(resp.Output),
328+
dec: json.NewDecoder(rdr),
263329
}
264330

265331
return d, nil
266332
}
267333

334+
func (api *UnixfsAPI) getSymlink(ctx context.Context, p path.Path, modTime time.Time) (files.Node, error) {
335+
resp, err := api.core().Request("cat", p.String()).
336+
Option("resolve-size", true).
337+
Option("stream", true).Send(ctx)
338+
if err != nil {
339+
return nil, err
340+
}
341+
if resp.Error != nil {
342+
return nil, resp.Error
343+
}
344+
345+
target, err := io.ReadAll(resp.Output)
346+
if err != nil {
347+
return nil, err
348+
}
349+
350+
return files.NewSymlinkFile(string(target), modTime), nil
351+
}
352+
268353
var (
269354
_ files.File = &apiFile{}
270355
_ files.Directory = &apiDir{}

client/rpc/unixfs.go

+13-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"errors"
77
"fmt"
88
"io"
9+
"os"
10+
"time"
911

1012
"github.com/ipfs/boxo/files"
1113
unixfs "github.com/ipfs/boxo/ipld/unixfs"
@@ -80,14 +82,13 @@ func (api *UnixfsAPI) Add(ctx context.Context, f files.Node, opts ...caopts.Unix
8082
}
8183
defer resp.Output.Close()
8284
dec := json.NewDecoder(resp.Output)
83-
loop:
85+
8486
for {
8587
var evt addEvent
86-
switch err := dec.Decode(&evt); err {
87-
case nil:
88-
case io.EOF:
89-
break loop
90-
default:
88+
if err := dec.Decode(&evt); err != nil {
89+
if errors.Is(err, io.EOF) {
90+
break
91+
}
9192
return path.ImmutablePath{}, err
9293
}
9394
out = evt
@@ -129,6 +130,9 @@ type lsLink struct {
129130
Size uint64
130131
Type unixfs_pb.Data_DataType
131132
Target string
133+
134+
Mode os.FileMode
135+
ModTime time.Time
132136
}
133137

134138
type lsObject struct {
@@ -222,6 +226,9 @@ func (api *UnixfsAPI) Ls(ctx context.Context, p path.Path, opts ...caopts.Unixfs
222226
Size: l0.Size,
223227
Type: ftype,
224228
Target: l0.Target,
229+
230+
Mode: l0.Mode,
231+
ModTime: l0.ModTime,
225232
}:
226233
case <-ctx.Done():
227234
}

0 commit comments

Comments
 (0)