github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/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  	"path/filepath"
    38  	"sync"
    39  
    40  	"camlistore.org/pkg/blob"
    41  	"camlistore.org/pkg/blobserver"
    42  	"camlistore.org/pkg/blobserver/local"
    43  	"camlistore.org/pkg/jsonconfig"
    44  	"camlistore.org/pkg/osutil"
    45  	"camlistore.org/pkg/types"
    46  )
    47  
    48  // DiskStorage implements the blobserver.Storage interface using the
    49  // local filesystem.
    50  type DiskStorage struct {
    51  	root string
    52  
    53  	// dirLockMu must be held for writing when deleting an empty directory
    54  	// and for read when receiving blobs.
    55  	dirLockMu *sync.RWMutex
    56  
    57  	// gen will be nil if partition != ""
    58  	gen *local.Generationer
    59  }
    60  
    61  func (ds *DiskStorage) String() string {
    62  	return fmt.Sprintf("\"filesystem\" file-per-blob at %s", ds.root)
    63  }
    64  
    65  // IsDir reports whether root is a localdisk (file-per-blob) storage directory.
    66  func IsDir(root string) (bool, error) {
    67  	if osutil.DirExists(filepath.Join(root, blob.RefFromString("").HashName())) {
    68  		return true, nil
    69  	}
    70  	return false, nil
    71  }
    72  
    73  // New returns a new local disk storage implementation at the provided
    74  // root directory, which must already exist.
    75  func New(root string) (*DiskStorage, error) {
    76  	// Local disk.
    77  	fi, err := os.Stat(root)
    78  	if os.IsNotExist(err) {
    79  		return nil, fmt.Errorf("Storage root %q doesn't exist", root)
    80  	}
    81  	if err != nil {
    82  		return nil, fmt.Errorf("Failed to stat directory %q: %v", root, err)
    83  	}
    84  	if !fi.IsDir() {
    85  		return nil, fmt.Errorf("Storage root %q exists but is not a directory.", root)
    86  	}
    87  	ds := &DiskStorage{
    88  		root:      root,
    89  		dirLockMu: new(sync.RWMutex),
    90  		gen:       local.NewGenerationer(root),
    91  	}
    92  	if err := ds.migrate3to2(); err != nil {
    93  		return nil, fmt.Errorf("Error updating localdisk format: %v", err)
    94  	}
    95  	if _, _, err := ds.StorageGeneration(); err != nil {
    96  		return nil, fmt.Errorf("Error initialization generation for %q: %v", root, err)
    97  	}
    98  	return ds, nil
    99  }
   100  
   101  func newFromConfig(_ blobserver.Loader, config jsonconfig.Obj) (storage blobserver.Storage, err error) {
   102  	path := config.RequiredString("path")
   103  	if err := config.Validate(); err != nil {
   104  		return nil, err
   105  	}
   106  	return New(path)
   107  }
   108  
   109  func init() {
   110  	blobserver.RegisterStorageConstructor("filesystem", blobserver.StorageConstructor(newFromConfig))
   111  }
   112  
   113  func (ds *DiskStorage) tryRemoveDir(dir string) {
   114  	ds.dirLockMu.Lock()
   115  	defer ds.dirLockMu.Unlock()
   116  	os.Remove(dir) // ignore error
   117  }
   118  
   119  func (ds *DiskStorage) Fetch(blob blob.Ref) (io.ReadCloser, uint32, error) {
   120  	fileName := ds.blobPath(blob)
   121  	stat, err := os.Stat(fileName)
   122  	if os.IsNotExist(err) {
   123  		return nil, 0, os.ErrNotExist
   124  	}
   125  	size := types.U32(stat.Size())
   126  	file, err := os.Open(fileName)
   127  	if err != nil {
   128  		if os.IsNotExist(err) {
   129  			err = os.ErrNotExist
   130  		}
   131  		return nil, 0, err
   132  	}
   133  	return file, size, nil
   134  }
   135  
   136  func (ds *DiskStorage) RemoveBlobs(blobs []blob.Ref) error {
   137  	for _, blob := range blobs {
   138  		fileName := ds.blobPath(blob)
   139  		err := os.Remove(fileName)
   140  		switch {
   141  		case err == nil:
   142  			continue
   143  		case os.IsNotExist(err):
   144  			// deleting already-deleted file; harmless.
   145  			continue
   146  		default:
   147  			return err
   148  		}
   149  	}
   150  	return nil
   151  }