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

creation of car from file / directory #246

Merged
merged 16 commits into from
Oct 21, 2021
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
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
29 changes: 29 additions & 0 deletions cmd/car/car.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ func main1() int {
Name: "car",
Usage: "Utility for working with car files",
Commands: []*cli.Command{
{
Name: "create",
Usage: "Create a car file",
Aliases: []string{"c"},
Action: CreateCar,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "file",
Aliases: []string{"f"},
Copy link
Member

@olizilla olizilla Oct 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-o --output would be mildly more famililar here and consistent with ipfs-car

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i was trying to follow the flags from tar, i'll add both as equivalent aliases.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, this is for creating a car not extracting. Ignore me. As you were.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tho, on second thoughts it does still feel more like an --output kind of a deal here. ipfs-car is using --output for "where to write the car to" when creating and "where to unpack the files to" when extracting.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Déjà vu! Oli and I had identical argument regarding ipfs-car, I said be like tar, Oli says tar is terrible, don't copy it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uh huh. "if you know how tar works then you know how car works" sounds compelling, and given our erudite nieche may be the right choice for the immediate audience.

My experience has been that tar is used as an example of a command that folks stuggle to remember what the flags do, so I aimed elsewhere. But we can have both. ipfs-car is not car and won't try to be, and folks can use the flavour that works for them.

Usage: "The car file to write to",
TakesFile: true,
},
},
},
{
Name: "detach-index",
Usage: "Detach an index to a detached file",
Expand Down Expand Up @@ -72,13 +86,28 @@ func main1() int {
Usage: "The type of index to write",
Value: multicodec.CarMultihashIndexSorted.String(),
},
&cli.BoolFlag{
Name: "v1",
Usage: "Write out only the carV1 file. Implies codec of 'none'",
},
},
},
{
Name: "list",
Aliases: []string{"l"},
Usage: "List the CIDs in a car",
Action: ListCar,
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "verbose",
Aliases: []string{"v"},
Usage: "Include verbose information about contained blocks",
},
&cli.BoolFlag{
Name: "unixfs",
Usage: "List unixfs filesystem from the root of the car",
},
},
},
{
Name: "verify",
Expand Down
119 changes: 119 additions & 0 deletions cmd/car/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package main

import (
"bytes"
"context"
"fmt"
"io"
"path"

blocks "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-unixfsnode/data/builder"
"github.com/ipld/go-car/v2"
"github.com/ipld/go-car/v2/blockstore"
dagpb "github.com/ipld/go-codec-dagpb"
"github.com/ipld/go-ipld-prime"
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
"github.com/multiformats/go-multicodec"
"github.com/multiformats/go-multihash"
"github.com/urfave/cli/v2"
)

// CreateCar creates a car
func CreateCar(c *cli.Context) error {
var err error
if c.Args().Len() == 0 {
return fmt.Errorf("a source location to build the car from must be specified")
}

if !c.IsSet("file") {
return fmt.Errorf("a file destination must be specified")
}

// make a cid with the right length that we eventually will patch with the root.
hasher, err := multihash.GetHasher(multihash.SHA2_256)
if err != nil {
return err
}
digest := hasher.Sum([]byte{})
hash, err := multihash.Encode(digest, multihash.SHA2_256)
if err != nil {
return err
}
proxyRoot := cid.NewCidV1(uint64(multicodec.DagPb), hash)

cdest, err := blockstore.OpenReadWrite(c.String("file"), []cid.Cid{proxyRoot})
if err != nil {
return err
}

// Write the unixfs blocks into the store.
root, err := writeFiles(c.Context, cdest, c.Args().Slice()...)
if err != nil {
return err
}

if err := cdest.Finalize(); err != nil {
return err
}
// re-open/finalize with the final root.
return car.ReplaceRootsInFile(c.String("file"), []cid.Cid{root})
}

func writeFiles(ctx context.Context, bs *blockstore.ReadWrite, paths ...string) (cid.Cid, error) {
ls := cidlink.DefaultLinkSystem()
ls.StorageReadOpener = func(_ ipld.LinkContext, l ipld.Link) (io.Reader, error) {
cl, ok := l.(cidlink.Link)
if !ok {
return nil, fmt.Errorf("not a cidlink")
}
blk, err := bs.Get(cl.Cid)
if err != nil {
return nil, err
}
return bytes.NewBuffer(blk.RawData()), nil
}
ls.StorageWriteOpener = func(_ ipld.LinkContext) (io.Writer, ipld.BlockWriteCommitter, error) {
buf := bytes.NewBuffer(nil)
return buf, func(l ipld.Link) error {
cl, ok := l.(cidlink.Link)
if !ok {
return fmt.Errorf("not a cidlink")
}
blk, err := blocks.NewBlockWithCid(buf.Bytes(), cl.Cid)
if err != nil {
return err
}
bs.Put(blk)
return nil
}, nil
}

topLevel := make([]dagpb.PBLink, 0, len(paths))
for _, p := range paths {
l, size, err := builder.BuildUnixFSRecursive(p, &ls)
if err != nil {
return cid.Undef, err
}
name := path.Base(p)
entry, err := builder.BuildUnixFSDirectoryEntry(name, int64(size), l)
if err != nil {
return cid.Undef, err
}
topLevel = append(topLevel, entry)
}

// make a directory for the file(s).

root, err := builder.BuildUnixFSDirectory(topLevel, &ls)
if err != nil {
return cid.Undef, nil
}
rcl, ok := root.(cidlink.Link)
if !ok {
return cid.Undef, fmt.Errorf("could not interpret %s", root)
}

return rcl.Cid, nil
}
2 changes: 1 addition & 1 deletion cmd/car/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func GetCarDag(c *cli.Context) error {
}

// selector traversal
s := selectorParser.CommonSelector_MatchAllRecursively
s, _ := selector.CompileSelector(selectorParser.CommonSelector_MatchAllRecursively)
if c.IsSet("selector") {
sn, err := selectorParser.ParseJSONSelector(c.String("selector"))
if err != nil {
Expand Down
17 changes: 17 additions & 0 deletions cmd/car/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,23 @@ func IndexCar(c *cli.Context) error {
}
defer r.Close()

if c.Bool("v1") {
if c.IsSet("codec") && c.String("codec") != "none" {
return fmt.Errorf("only supported codec for a v1 car is 'none'")
}
outStream := os.Stdout
if c.Args().Len() >= 2 {
outStream, err = os.Create(c.Args().Get(1))
if err != nil {
return err
}
}
defer outStream.Close()

_, err := io.Copy(outStream, r.DataReader())
return err
}

var idx index.Index
if c.String("codec") != "none" {
var mc multicodec.Code
Expand Down
Loading