You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
208 lines
4.2 KiB
208 lines
4.2 KiB
package localfs
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"time"
|
|
|
|
"github.com/andreimarcu/linx-server/backends"
|
|
"github.com/andreimarcu/linx-server/helpers"
|
|
"github.com/andreimarcu/linx-server/torrent"
|
|
)
|
|
|
|
type LocalfsBackend struct {
|
|
metaPath string
|
|
filesPath string
|
|
}
|
|
|
|
type MetadataJSON struct {
|
|
DeleteKey string `json:"delete_key"`
|
|
Sha256sum string `json:"sha256sum"`
|
|
Mimetype string `json:"mimetype"`
|
|
Size int64 `json:"size"`
|
|
Expiry int64 `json:"expiry"`
|
|
ArchiveFiles []string `json:"archive_files,omitempty"`
|
|
}
|
|
|
|
func (b LocalfsBackend) Delete(key string) (err error) {
|
|
err = os.Remove(path.Join(b.filesPath, key))
|
|
if err != nil {
|
|
return
|
|
}
|
|
err = os.Remove(path.Join(b.metaPath, key))
|
|
return
|
|
}
|
|
|
|
func (b LocalfsBackend) Exists(key string) (bool, error) {
|
|
_, err := os.Stat(path.Join(b.filesPath, key))
|
|
return err == nil, err
|
|
}
|
|
|
|
func (b LocalfsBackend) Head(key string) (metadata backends.Metadata, err error) {
|
|
f, err := os.Open(path.Join(b.metaPath, key))
|
|
if os.IsNotExist(err) {
|
|
return metadata, backends.NotFoundErr
|
|
} else if err != nil {
|
|
return metadata, backends.BadMetadata
|
|
}
|
|
defer f.Close()
|
|
|
|
decoder := json.NewDecoder(f)
|
|
|
|
mjson := MetadataJSON{}
|
|
if err := decoder.Decode(&mjson); err != nil {
|
|
return metadata, backends.BadMetadata
|
|
}
|
|
|
|
metadata.DeleteKey = mjson.DeleteKey
|
|
metadata.Mimetype = mjson.Mimetype
|
|
metadata.ArchiveFiles = mjson.ArchiveFiles
|
|
metadata.Sha256sum = mjson.Sha256sum
|
|
metadata.Expiry = time.Unix(mjson.Expiry, 0)
|
|
metadata.Size = mjson.Size
|
|
|
|
return
|
|
}
|
|
|
|
func (b LocalfsBackend) Get(key string) (metadata backends.Metadata, f io.ReadCloser, err error) {
|
|
metadata, err = b.Head(key)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
f, err = os.Open(path.Join(b.filesPath, key))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (b LocalfsBackend) writeMetadata(key string, metadata backends.Metadata) error {
|
|
metaPath := path.Join(b.metaPath, key)
|
|
|
|
mjson := MetadataJSON{
|
|
DeleteKey: metadata.DeleteKey,
|
|
Mimetype: metadata.Mimetype,
|
|
ArchiveFiles: metadata.ArchiveFiles,
|
|
Sha256sum: metadata.Sha256sum,
|
|
Expiry: metadata.Expiry.Unix(),
|
|
Size: metadata.Size,
|
|
}
|
|
|
|
dst, err := os.Create(metaPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer dst.Close()
|
|
|
|
encoder := json.NewEncoder(dst)
|
|
err = encoder.Encode(mjson)
|
|
if err != nil {
|
|
os.Remove(metaPath)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (b LocalfsBackend) Put(key string, r io.Reader, expiry time.Time, deleteKey string) (m backends.Metadata, err error) {
|
|
filePath := path.Join(b.filesPath, key)
|
|
|
|
dst, err := os.Create(filePath)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer dst.Close()
|
|
|
|
bytes, err := io.Copy(dst, r)
|
|
if bytes == 0 {
|
|
os.Remove(filePath)
|
|
return m, backends.FileEmptyError
|
|
} else if err != nil {
|
|
os.Remove(filePath)
|
|
return m, err
|
|
}
|
|
|
|
m.Expiry = expiry
|
|
m.DeleteKey = deleteKey
|
|
m.Size = bytes
|
|
m.Mimetype, _ = helpers.DetectMime(dst)
|
|
m.Sha256sum, _ = helpers.Sha256sum(dst)
|
|
m.ArchiveFiles, _ = helpers.ListArchiveFiles(m.Mimetype, m.Size, dst)
|
|
|
|
err = b.writeMetadata(key, m)
|
|
if err != nil {
|
|
os.Remove(filePath)
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (b LocalfsBackend) Size(key string) (int64, error) {
|
|
fileInfo, err := os.Stat(path.Join(b.filesPath, key))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return fileInfo.Size(), nil
|
|
}
|
|
|
|
func (b LocalfsBackend) GetTorrent(fileName string, url string) (t torrent.Torrent, err error) {
|
|
chunk := make([]byte, torrent.TORRENT_PIECE_LENGTH)
|
|
|
|
t = torrent.Torrent{
|
|
Encoding: "UTF-8",
|
|
Info: torrent.TorrentInfo{
|
|
PieceLength: torrent.TORRENT_PIECE_LENGTH,
|
|
Name: fileName,
|
|
},
|
|
UrlList: []string{url},
|
|
}
|
|
|
|
_, f, err := b.Get(fileName)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer f.Close()
|
|
|
|
for {
|
|
n, err := f.Read(chunk)
|
|
if err == io.EOF {
|
|
break
|
|
} else if err != nil {
|
|
return t, err
|
|
}
|
|
|
|
t.Info.Length += n
|
|
t.Info.Pieces += string(torrent.HashPiece(chunk[:n]))
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (b LocalfsBackend) List() ([]string, error) {
|
|
var output []string
|
|
|
|
files, err := ioutil.ReadDir(b.filesPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, file := range files {
|
|
output = append(output, file.Name())
|
|
}
|
|
|
|
return output, nil
|
|
}
|
|
|
|
func NewLocalfsBackend(metaPath string, filesPath string) LocalfsBackend {
|
|
return LocalfsBackend{
|
|
metaPath: metaPath,
|
|
filesPath: filesPath,
|
|
}
|
|
}
|