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, } }