mirror of https://github.com/perkeep/perkeep.git
208 lines
5.4 KiB
Go
208 lines
5.4 KiB
Go
/*
|
|
Copyright 2011 The Perkeep Authors
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha1"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"hash"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
|
|
"perkeep.org/pkg/blob"
|
|
"perkeep.org/pkg/client"
|
|
"perkeep.org/pkg/cmdmain"
|
|
"perkeep.org/pkg/constants"
|
|
"perkeep.org/pkg/schema"
|
|
)
|
|
|
|
type blobCmd struct {
|
|
title string
|
|
tag string
|
|
|
|
makePermanode bool // make new, unique permanode of the blob
|
|
hashFunc string // empty means automatic
|
|
}
|
|
|
|
func init() {
|
|
cmdmain.RegisterMode("blob", func(flags *flag.FlagSet) cmdmain.CommandRunner {
|
|
cmd := new(blobCmd)
|
|
flags.BoolVar(&cmd.makePermanode, "permanode", false, "Create and associate a new permanode for the blob.")
|
|
flags.StringVar(&cmd.title, "title", "", "Optional title attribute to set on permanode when using -permanode.")
|
|
flags.StringVar(&cmd.tag, "tag", "", "Optional tag(s) to set on permanode when using -permanode. Single value or comma separated.")
|
|
flags.StringVar(&cmd.hashFunc, "hash", "", "Name of hash algorithm to use. Empty means to use the current recommended one.")
|
|
return cmd
|
|
})
|
|
}
|
|
|
|
func (c *blobCmd) Describe() string {
|
|
return "Upload raw blob(s)."
|
|
}
|
|
|
|
func (c *blobCmd) Usage() {
|
|
fmt.Fprintf(cmdmain.Stderr, "Usage: pk-put [globalopts] blob <files>\n pk-put [globalopts] blob -\n")
|
|
}
|
|
|
|
func (c *blobCmd) Examples() []string {
|
|
return []string{
|
|
"<files> (raw, without any metadata)",
|
|
"--permanode --title='Homedir backup' --tag=backup,homedir $HOME",
|
|
"- (read from stdin)",
|
|
}
|
|
}
|
|
|
|
func (c *blobCmd) RunCommand(args []string) error {
|
|
if len(args) == 0 {
|
|
return errors.New("no files given")
|
|
}
|
|
if c.title != "" && !c.makePermanode {
|
|
return cmdmain.UsageError("Can't set title without using --permanode")
|
|
}
|
|
if c.tag != "" && !c.makePermanode {
|
|
return cmdmain.UsageError("Can't set tag without using --permanode")
|
|
}
|
|
|
|
up := getUploader()
|
|
if c.makePermanode {
|
|
testSigBlobRef := up.Client.SignerPublicKeyBlobref()
|
|
if !testSigBlobRef.Valid() {
|
|
return cmdmain.UsageError("A GPG key is needed to create permanodes; configure one or use vivify mode.")
|
|
}
|
|
}
|
|
|
|
var (
|
|
handle *client.UploadHandle
|
|
permaNode *client.PutResult
|
|
put *client.PutResult
|
|
err error
|
|
)
|
|
|
|
for _, arg := range args {
|
|
if arg == "-" {
|
|
handle, err = stdinBlobHandle()
|
|
} else {
|
|
handle, err = c.fileBlobHandle(up, arg)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
put, err = up.Upload(ctxbg, handle)
|
|
handleResult("blob", put, err)
|
|
continue
|
|
}
|
|
|
|
if c.makePermanode {
|
|
permaNode, err = up.UploadNewPermanode(ctxbg)
|
|
if err != nil {
|
|
return fmt.Errorf("Uploading permanode: %v", err)
|
|
}
|
|
}
|
|
|
|
if permaNode != nil && put != nil {
|
|
put, err := up.UploadAndSignBlob(ctxbg, schema.NewSetAttributeClaim(permaNode.BlobRef, "camliContent", put.BlobRef.String()))
|
|
if handleResult("claim-permanode-content", put, err) != nil {
|
|
return err
|
|
}
|
|
if c.title != "" {
|
|
put, err := up.UploadAndSignBlob(ctxbg, schema.NewSetAttributeClaim(permaNode.BlobRef, "title", c.title))
|
|
handleResult("claim-permanode-title", put, err)
|
|
}
|
|
if c.tag != "" {
|
|
tags := strings.Split(c.tag, ",")
|
|
for _, tag := range tags {
|
|
m := schema.NewAddAttributeClaim(permaNode.BlobRef, "tag", tag)
|
|
put, err := up.UploadAndSignBlob(ctxbg, m)
|
|
handleResult("claim-permanode-tag", put, err)
|
|
}
|
|
}
|
|
handleResult("permanode", permaNode, nil)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func stdinBlobHandle() (*client.UploadHandle, error) {
|
|
var buf bytes.Buffer
|
|
size, err := io.CopyN(&buf, cmdmain.Stdin, constants.MaxBlobSize+1)
|
|
if size > constants.MaxBlobSize {
|
|
return nil, fmt.Errorf("blob size cannot be bigger than %d", constants.MaxBlobSize)
|
|
}
|
|
if err != nil && err != io.EOF {
|
|
return nil, err
|
|
}
|
|
h := blob.NewHash()
|
|
if _, err := h.Write(buf.Bytes()); err != nil {
|
|
return nil, err
|
|
}
|
|
return &client.UploadHandle{
|
|
BlobRef: blob.RefFromHash(h),
|
|
Size: uint32(buf.Len()),
|
|
Contents: &buf,
|
|
}, nil
|
|
}
|
|
|
|
func (c *blobCmd) fileBlobHandle(up *Uploader, path string) (*client.UploadHandle, error) {
|
|
fi, err := up.stat(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if fi.Mode()&os.ModeType != 0 {
|
|
return nil, fmt.Errorf("%q is not a regular file", path)
|
|
}
|
|
size := fi.Size()
|
|
if size > constants.MaxBlobSize {
|
|
return nil, fmt.Errorf("blob size cannot be bigger than %d", constants.MaxBlobSize)
|
|
}
|
|
file, err := up.open(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer file.Close()
|
|
|
|
buf := make([]byte, size)
|
|
if _, err := io.ReadFull(file, buf); err != nil {
|
|
return nil, err
|
|
}
|
|
h := c.newHash()
|
|
if _, err := h.Write(buf); err != nil {
|
|
return nil, err
|
|
}
|
|
return &client.UploadHandle{
|
|
BlobRef: blob.RefFromHash(h),
|
|
Size: uint32(size),
|
|
Contents: bytes.NewReader(buf),
|
|
}, nil
|
|
}
|
|
|
|
func (c *blobCmd) newHash() hash.Hash {
|
|
switch c.hashFunc {
|
|
case "":
|
|
return blob.NewHash()
|
|
case "sha1":
|
|
return sha1.New()
|
|
default:
|
|
// TODO: move all this into the blob package?
|
|
log.Fatalf("unsupported hash function %q", c.hashFunc)
|
|
panic("unreachable")
|
|
}
|
|
}
|