github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/pkg/blobserver/localdisk/localdisk.go (about)

     1  /*
     2  Copyright 2011 Google Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  /*
    18  Package localdisk registers the "filesystem" blobserver storage type,
    19  storing blobs in a forest of sharded directories at the specified root.
    20  
    21  Example low-level config:
    22  
    23       "/storage/": {
    24           "handler": "storage-filesystem",
    25           "handlerArgs": {
    26              "path": "/var/camlistore/blobs"
    27            }
    28       },
    29  
    30  */
    31  package localdisk
    32  
    33  import (
    34  	"fmt"
    35  	"io"
    36  	"os"
    37  	"sync"
    38  
    39  	"camlistore.org/pkg/blob"
    40  	"camlistore.org/pkg/blobserver"
    41  	"camlistore.org/pkg/blobserver/local"
    42  	"camlistore.org/pkg/jsonconfig"
    43  	"camlistore.org/pkg/types"
    44  )
    45  
    46  // DiskStorage implements the blobserver.Storage interface using the
    47  // local filesystem.
    48  type DiskStorage struct {
    49  	root string
    50  
    51  	// dirLockMu must be held for writing when deleting an empty directory
    52  	// and for read when receiving blobs.
    53  	dirLockMu *sync.RWMutex
    54  
    55  	// gen will be nil if partition != ""
    56  	gen *local.Generationer
    57  }
    58  
    59  // New returns a new local disk storage implementation at the provided
    60  // root directory, which must already exist.
    61  func New(root string) (*DiskStorage, error) {
    62  	// Local disk.
    63  	fi, err := os.Stat(root)
    64  	if os.IsNotExist(err) {
    65  		return nil, fmt.Errorf("Storage root %q doesn't exist", root)
    66  	}
    67  	if err != nil {
    68  		return nil, fmt.Errorf("Failed to stat directory %q: %v", root, err)
    69  	}
    70  	if !fi.IsDir() {
    71  		return nil, fmt.Errorf("Storage root %q exists but is not a directory.", root)
    72  	}
    73  	ds := &DiskStorage{
    74  		root:      root,
    75  		dirLockMu: new(sync.RWMutex),
    76  		gen:       local.NewGenerationer(root),
    77  	}
    78  	if err := ds.migrate3to2(); err != nil {
    79  		return nil, fmt.Errorf("Error updating localdisk format: %v", err)
    80  	}
    81  	if _, _, err := ds.StorageGeneration(); err != nil {
    82  		return nil, fmt.Errorf("Error initialization generation for %q: %v", root, err)
    83  	}
    84  	return ds, nil
    85  }
    86  
    87  func newFromConfig(_ blobserver.Loader, config jsonconfig.Obj) (storage blobserver.Storage, err error) {
    88  	path := config.RequiredString("path")
    89  	if err := config.Validate(); err != nil {
    90  		return nil, err
    91  	}
    92  	return New(path)
    93  }
    94  
    95  func init() {
    96  	blobserver.RegisterStorageConstructor("filesystem", blobserver.StorageConstructor(newFromConfig))
    97  }
    98  
    99  func (ds *DiskStorage) tryRemoveDir(dir string) {
   100  	ds.dirLockMu.Lock()
   101  	defer ds.dirLockMu.Unlock()
   102  	os.Remove(dir) // ignore error
   103  }
   104  
   105  func (ds *DiskStorage) FetchStreaming(blob blob.Ref) (io.ReadCloser, int64, error) {
   106  	return ds.Fetch(blob)
   107  }
   108  
   109  func (ds *DiskStorage) Fetch(blob blob.Ref) (types.ReadSeekCloser, int64, error) {
   110  	fileName := ds.blobPath(blob)
   111  	stat, err := os.Stat(fileName)
   112  	if os.IsNotExist(err) {
   113  		return nil, 0, os.ErrNotExist
   114  	}
   115  	file, err := os.Open(fileName)
   116  	if err != nil {
   117  		if os.IsNotExist(err) {
   118  			err = os.ErrNotExist
   119  		}
   120  		return nil, 0, err
   121  	}
   122  	return file, stat.Size(), nil
   123  }
   124  
   125  func (ds *DiskStorage) RemoveBlobs(blobs []blob.Ref) error {
   126  	for _, blob := range blobs {
   127  		fileName := ds.blobPath(blob)
   128  		err := os.Remove(fileName)
   129  		switch {
   130  		case err == nil:
   131  			continue
   132  		case os.IsNotExist(err):
   133  			// deleting already-deleted file; harmless.
   134  			continue
   135  		default:
   136  			return err
   137  		}
   138  	}
   139  	return nil
   140  }