github.com/df-mc/goleveldb@v1.1.9/leveldb/storage/file_storage.go (about)

     1  // Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
     2  // All rights reservefs.
     3  //
     4  // Use of this source code is governed by a BSD-style license that can be
     5  // found in the LICENSE file.
     6  
     7  package storage
     8  
     9  import (
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"io/ioutil"
    14  	"os"
    15  	"path/filepath"
    16  	"runtime"
    17  	"sort"
    18  	"strconv"
    19  	"strings"
    20  	"sync"
    21  	"time"
    22  )
    23  
    24  var (
    25  	errFileOpen = errors.New("leveldb/storage: file still open")
    26  	errReadOnly = errors.New("leveldb/storage: storage is read-only")
    27  )
    28  
    29  type fileLock interface {
    30  	release() error
    31  }
    32  
    33  type fileStorageLock struct {
    34  	fs *fileStorage
    35  }
    36  
    37  func (lock *fileStorageLock) Unlock() {
    38  	if lock.fs != nil {
    39  		lock.fs.mu.Lock()
    40  		defer lock.fs.mu.Unlock()
    41  		if lock.fs.slock == lock {
    42  			lock.fs.slock = nil
    43  		}
    44  	}
    45  }
    46  
    47  type int64Slice []int64
    48  
    49  func (p int64Slice) Len() int           { return len(p) }
    50  func (p int64Slice) Less(i, j int) bool { return p[i] < p[j] }
    51  func (p int64Slice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
    52  
    53  func writeFileSynced(filename string, data []byte, perm os.FileMode) error {
    54  	f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
    55  	if err != nil {
    56  		return err
    57  	}
    58  	n, err := f.Write(data)
    59  	if err == nil && n < len(data) {
    60  		err = io.ErrShortWrite
    61  	}
    62  	if err1 := f.Sync(); err == nil {
    63  		err = err1
    64  	}
    65  	if err1 := f.Close(); err == nil {
    66  		err = err1
    67  	}
    68  	return err
    69  }
    70  
    71  const logSizeThreshold = 1024 * 1024 // 1 MiB
    72  
    73  // fileStorage is a file-system backed storage.
    74  type fileStorage struct {
    75  	path     string
    76  	readOnly bool
    77  
    78  	mu      sync.Mutex
    79  	flock   fileLock
    80  	slock   *fileStorageLock
    81  	logw    *os.File
    82  	logSize int64
    83  	buf     []byte
    84  	// Opened file counter; if open < 0 means closed.
    85  	open int
    86  	day  int
    87  }
    88  
    89  // OpenFile returns a new filesystem-backed storage implementation with the given
    90  // path. This also acquire a file lock, so any subsequent attempt to open the
    91  // same path will fail.
    92  //
    93  // The storage must be closed after use, by calling Close method.
    94  func OpenFile(path string, readOnly bool) (Storage, error) {
    95  	if fi, err := os.Stat(path); err == nil {
    96  		if !fi.IsDir() {
    97  			return nil, fmt.Errorf("leveldb/storage: open %s: not a directory", path)
    98  		}
    99  	} else if os.IsNotExist(err) && !readOnly {
   100  		if err := os.MkdirAll(path, 0755); err != nil {
   101  			return nil, err
   102  		}
   103  	} else {
   104  		return nil, err
   105  	}
   106  
   107  	flock, err := newFileLock(filepath.Join(path, "LOCK"), readOnly)
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  
   112  	defer func() {
   113  		if err != nil {
   114  			flock.release()
   115  		}
   116  	}()
   117  
   118  	var (
   119  		logw    *os.File
   120  		logSize int64
   121  	)
   122  	if !readOnly {
   123  		logw, err = os.OpenFile(filepath.Join(path, "LOG"), os.O_WRONLY|os.O_CREATE, 0644)
   124  		if err != nil {
   125  			return nil, err
   126  		}
   127  		logSize, err = logw.Seek(0, os.SEEK_END)
   128  		if err != nil {
   129  			logw.Close()
   130  			return nil, err
   131  		}
   132  	}
   133  
   134  	fs := &fileStorage{
   135  		path:     path,
   136  		readOnly: readOnly,
   137  		flock:    flock,
   138  		logw:     logw,
   139  		logSize:  logSize,
   140  	}
   141  	runtime.SetFinalizer(fs, (*fileStorage).Close)
   142  	return fs, nil
   143  }
   144  
   145  func (fs *fileStorage) Lock() (Locker, error) {
   146  	fs.mu.Lock()
   147  	defer fs.mu.Unlock()
   148  	if fs.open < 0 {
   149  		return nil, ErrClosed
   150  	}
   151  	if fs.readOnly {
   152  		return &fileStorageLock{}, nil
   153  	}
   154  	if fs.slock != nil {
   155  		return nil, ErrLocked
   156  	}
   157  	fs.slock = &fileStorageLock{fs: fs}
   158  	return fs.slock, nil
   159  }
   160  
   161  func itoa(buf []byte, i int, wid int) []byte {
   162  	u := uint(i)
   163  	if u == 0 && wid <= 1 {
   164  		return append(buf, '0')
   165  	}
   166  
   167  	// Assemble decimal in reverse order.
   168  	var b [32]byte
   169  	bp := len(b)
   170  	for ; u > 0 || wid > 0; u /= 10 {
   171  		bp--
   172  		wid--
   173  		b[bp] = byte(u%10) + '0'
   174  	}
   175  	return append(buf, b[bp:]...)
   176  }
   177  
   178  func (fs *fileStorage) printDay(t time.Time) {
   179  	if fs.day == t.Day() {
   180  		return
   181  	}
   182  	fs.day = t.Day()
   183  	fs.logw.Write([]byte("=============== " + t.Format("Jan 2, 2006 (MST)") + " ===============\n"))
   184  }
   185  
   186  func (fs *fileStorage) doLog(t time.Time, str string) {
   187  	if fs.logSize > logSizeThreshold {
   188  		// Rotate log file.
   189  		fs.logw.Close()
   190  		fs.logw = nil
   191  		fs.logSize = 0
   192  		rename(filepath.Join(fs.path, "LOG"), filepath.Join(fs.path, "LOG.old"))
   193  	}
   194  	if fs.logw == nil {
   195  		var err error
   196  		fs.logw, err = os.OpenFile(filepath.Join(fs.path, "LOG"), os.O_WRONLY|os.O_CREATE, 0644)
   197  		if err != nil {
   198  			return
   199  		}
   200  		// Force printDay on new log file.
   201  		fs.day = 0
   202  	}
   203  	fs.printDay(t)
   204  	hour, min, sec := t.Clock()
   205  	msec := t.Nanosecond() / 1e3
   206  	// time
   207  	fs.buf = itoa(fs.buf[:0], hour, 2)
   208  	fs.buf = append(fs.buf, ':')
   209  	fs.buf = itoa(fs.buf, min, 2)
   210  	fs.buf = append(fs.buf, ':')
   211  	fs.buf = itoa(fs.buf, sec, 2)
   212  	fs.buf = append(fs.buf, '.')
   213  	fs.buf = itoa(fs.buf, msec, 6)
   214  	fs.buf = append(fs.buf, ' ')
   215  	// write
   216  	fs.buf = append(fs.buf, []byte(str)...)
   217  	fs.buf = append(fs.buf, '\n')
   218  	n, _ := fs.logw.Write(fs.buf)
   219  	fs.logSize += int64(n)
   220  }
   221  
   222  func (fs *fileStorage) Log(str string) {
   223  	if !fs.readOnly {
   224  		t := time.Now()
   225  		fs.mu.Lock()
   226  		defer fs.mu.Unlock()
   227  		if fs.open < 0 {
   228  			return
   229  		}
   230  		fs.doLog(t, str)
   231  	}
   232  }
   233  
   234  func (fs *fileStorage) log(str string) {
   235  	if !fs.readOnly {
   236  		fs.doLog(time.Now(), str)
   237  	}
   238  }
   239  
   240  func (fs *fileStorage) setMeta(fd FileDesc) error {
   241  	content := fsGenName(fd) + "\n"
   242  	// Check and backup old CURRENT file.
   243  	currentPath := filepath.Join(fs.path, "CURRENT")
   244  	if _, err := os.Stat(currentPath); err == nil {
   245  		b, err := ioutil.ReadFile(currentPath)
   246  		if err != nil {
   247  			fs.log(fmt.Sprintf("backup CURRENT: %v", err))
   248  			return err
   249  		}
   250  		if string(b) == content {
   251  			// Content not changed, do nothing.
   252  			return nil
   253  		}
   254  		if err := writeFileSynced(currentPath+".bak", b, 0644); err != nil {
   255  			fs.log(fmt.Sprintf("backup CURRENT: %v", err))
   256  			return err
   257  		}
   258  	} else if !os.IsNotExist(err) {
   259  		return err
   260  	}
   261  	path := fmt.Sprintf("%s.%d", filepath.Join(fs.path, "CURRENT"), fd.Num)
   262  	if err := writeFileSynced(path, []byte(content), 0644); err != nil {
   263  		fs.log(fmt.Sprintf("create CURRENT.%d: %v", fd.Num, err))
   264  		return err
   265  	}
   266  	// Replace CURRENT file.
   267  	if err := rename(path, currentPath); err != nil {
   268  		fs.log(fmt.Sprintf("rename CURRENT.%d: %v", fd.Num, err))
   269  		return err
   270  	}
   271  	// Sync root directory.
   272  	if err := syncDir(fs.path); err != nil {
   273  		fs.log(fmt.Sprintf("syncDir: %v", err))
   274  		return err
   275  	}
   276  	return nil
   277  }
   278  
   279  func (fs *fileStorage) SetMeta(fd FileDesc) error {
   280  	if !FileDescOk(fd) {
   281  		return ErrInvalidFile
   282  	}
   283  	if fs.readOnly {
   284  		return errReadOnly
   285  	}
   286  
   287  	fs.mu.Lock()
   288  	defer fs.mu.Unlock()
   289  	if fs.open < 0 {
   290  		return ErrClosed
   291  	}
   292  	return fs.setMeta(fd)
   293  }
   294  
   295  func (fs *fileStorage) GetMeta() (FileDesc, error) {
   296  	fs.mu.Lock()
   297  	defer fs.mu.Unlock()
   298  	if fs.open < 0 {
   299  		return FileDesc{}, ErrClosed
   300  	}
   301  	dir, err := os.Open(fs.path)
   302  	if err != nil {
   303  		return FileDesc{}, err
   304  	}
   305  	names, err := dir.Readdirnames(0)
   306  	// Close the dir first before checking for Readdirnames error.
   307  	if ce := dir.Close(); ce != nil {
   308  		fs.log(fmt.Sprintf("close dir: %v", ce))
   309  	}
   310  	if err != nil {
   311  		return FileDesc{}, err
   312  	}
   313  	// Try this in order:
   314  	// - CURRENT.[0-9]+ ('pending rename' file, descending order)
   315  	// - CURRENT
   316  	// - CURRENT.bak
   317  	//
   318  	// Skip corrupted file or file that point to a missing target file.
   319  	type currentFile struct {
   320  		name string
   321  		fd   FileDesc
   322  	}
   323  	tryCurrent := func(name string) (*currentFile, error) {
   324  		b, err := ioutil.ReadFile(filepath.Join(fs.path, name))
   325  		if err != nil {
   326  			if os.IsNotExist(err) {
   327  				err = os.ErrNotExist
   328  			}
   329  			return nil, err
   330  		}
   331  		var fd FileDesc
   332  		if len(b) < 1 || b[len(b)-1] != '\n' || !fsParseNamePtr(string(b[:len(b)-1]), &fd) {
   333  			fs.log(fmt.Sprintf("%s: corrupted content: %q", name, b))
   334  			err := &ErrCorrupted{
   335  				Err: errors.New("leveldb/storage: corrupted or incomplete CURRENT file"),
   336  			}
   337  			return nil, err
   338  		}
   339  		if _, err := os.Stat(filepath.Join(fs.path, fsGenName(fd))); err != nil {
   340  			if os.IsNotExist(err) {
   341  				fs.log(fmt.Sprintf("%s: missing target file: %s", name, fd))
   342  				err = os.ErrNotExist
   343  			}
   344  			return nil, err
   345  		}
   346  		return &currentFile{name: name, fd: fd}, nil
   347  	}
   348  	tryCurrents := func(names []string) (*currentFile, error) {
   349  		var (
   350  			cur *currentFile
   351  			// Last corruption error.
   352  			lastCerr error
   353  		)
   354  		for _, name := range names {
   355  			var err error
   356  			cur, err = tryCurrent(name)
   357  			if err == nil {
   358  				break
   359  			} else if err == os.ErrNotExist {
   360  				// Fallback to the next file.
   361  			} else if isCorrupted(err) {
   362  				lastCerr = err
   363  				// Fallback to the next file.
   364  			} else {
   365  				// In case the error is due to permission, etc.
   366  				return nil, err
   367  			}
   368  		}
   369  		if cur == nil {
   370  			err := os.ErrNotExist
   371  			if lastCerr != nil {
   372  				err = lastCerr
   373  			}
   374  			return nil, err
   375  		}
   376  		return cur, nil
   377  	}
   378  
   379  	// Try 'pending rename' files.
   380  	var nums []int64
   381  	for _, name := range names {
   382  		if strings.HasPrefix(name, "CURRENT.") && name != "CURRENT.bak" {
   383  			i, err := strconv.ParseInt(name[8:], 10, 64)
   384  			if err == nil {
   385  				nums = append(nums, i)
   386  			}
   387  		}
   388  	}
   389  	var (
   390  		pendCur   *currentFile
   391  		pendErr   = os.ErrNotExist
   392  		pendNames []string
   393  	)
   394  	if len(nums) > 0 {
   395  		sort.Sort(sort.Reverse(int64Slice(nums)))
   396  		pendNames = make([]string, len(nums))
   397  		for i, num := range nums {
   398  			pendNames[i] = fmt.Sprintf("CURRENT.%d", num)
   399  		}
   400  		pendCur, pendErr = tryCurrents(pendNames)
   401  		if pendErr != nil && pendErr != os.ErrNotExist && !isCorrupted(pendErr) {
   402  			return FileDesc{}, pendErr
   403  		}
   404  	}
   405  
   406  	// Try CURRENT and CURRENT.bak.
   407  	curCur, curErr := tryCurrents([]string{"CURRENT", "CURRENT.bak"})
   408  	if curErr != nil && curErr != os.ErrNotExist && !isCorrupted(curErr) {
   409  		return FileDesc{}, curErr
   410  	}
   411  
   412  	// pendCur takes precedence, but guards against obsolete pendCur.
   413  	if pendCur != nil && (curCur == nil || pendCur.fd.Num > curCur.fd.Num) {
   414  		curCur = pendCur
   415  	}
   416  
   417  	if curCur != nil {
   418  		// Restore CURRENT file to proper state.
   419  		if !fs.readOnly && (curCur.name != "CURRENT" || len(pendNames) != 0) {
   420  			// Ignore setMeta errors, however don't delete obsolete files if we
   421  			// catch error.
   422  			if err := fs.setMeta(curCur.fd); err == nil {
   423  				// Remove 'pending rename' files.
   424  				for _, name := range pendNames {
   425  					if err := os.Remove(filepath.Join(fs.path, name)); err != nil {
   426  						fs.log(fmt.Sprintf("remove %s: %v", name, err))
   427  					}
   428  				}
   429  			}
   430  		}
   431  		return curCur.fd, nil
   432  	}
   433  
   434  	// Nothing found.
   435  	if isCorrupted(pendErr) {
   436  		return FileDesc{}, pendErr
   437  	}
   438  	return FileDesc{}, curErr
   439  }
   440  
   441  func (fs *fileStorage) List(ft FileType) (fds []FileDesc, err error) {
   442  	fs.mu.Lock()
   443  	defer fs.mu.Unlock()
   444  	if fs.open < 0 {
   445  		return nil, ErrClosed
   446  	}
   447  	dir, err := os.Open(fs.path)
   448  	if err != nil {
   449  		return
   450  	}
   451  	names, err := dir.Readdirnames(0)
   452  	// Close the dir first before checking for Readdirnames error.
   453  	if cerr := dir.Close(); cerr != nil {
   454  		fs.log(fmt.Sprintf("close dir: %v", cerr))
   455  	}
   456  	if err == nil {
   457  		for _, name := range names {
   458  			if fd, ok := fsParseName(name); ok && fd.Type&ft != 0 {
   459  				fds = append(fds, fd)
   460  			}
   461  		}
   462  	}
   463  	return
   464  }
   465  
   466  func (fs *fileStorage) Open(fd FileDesc) (Reader, error) {
   467  	if !FileDescOk(fd) {
   468  		return nil, ErrInvalidFile
   469  	}
   470  
   471  	fs.mu.Lock()
   472  	defer fs.mu.Unlock()
   473  	if fs.open < 0 {
   474  		return nil, ErrClosed
   475  	}
   476  	of, err := os.OpenFile(filepath.Join(fs.path, fsGenName(fd)), os.O_RDONLY, 0)
   477  	if err != nil {
   478  		if fsHasOldName(fd) && os.IsNotExist(err) {
   479  			of, err = os.OpenFile(filepath.Join(fs.path, fsGenOldName(fd)), os.O_RDONLY, 0)
   480  			if err == nil {
   481  				goto ok
   482  			}
   483  		}
   484  		return nil, err
   485  	}
   486  ok:
   487  	fs.open++
   488  	return &fileWrap{File: of, fs: fs, fd: fd}, nil
   489  }
   490  
   491  func (fs *fileStorage) Create(fd FileDesc) (Writer, error) {
   492  	if !FileDescOk(fd) {
   493  		return nil, ErrInvalidFile
   494  	}
   495  	if fs.readOnly {
   496  		return nil, errReadOnly
   497  	}
   498  
   499  	fs.mu.Lock()
   500  	defer fs.mu.Unlock()
   501  	if fs.open < 0 {
   502  		return nil, ErrClosed
   503  	}
   504  	of, err := os.OpenFile(filepath.Join(fs.path, fsGenName(fd)), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
   505  	if err != nil {
   506  		return nil, err
   507  	}
   508  	fs.open++
   509  	return &fileWrap{File: of, fs: fs, fd: fd}, nil
   510  }
   511  
   512  func (fs *fileStorage) Remove(fd FileDesc) error {
   513  	if !FileDescOk(fd) {
   514  		return ErrInvalidFile
   515  	}
   516  	if fs.readOnly {
   517  		return errReadOnly
   518  	}
   519  
   520  	fs.mu.Lock()
   521  	defer fs.mu.Unlock()
   522  	if fs.open < 0 {
   523  		return ErrClosed
   524  	}
   525  	err := os.Remove(filepath.Join(fs.path, fsGenName(fd)))
   526  	if err != nil {
   527  		if fsHasOldName(fd) && os.IsNotExist(err) {
   528  			if e1 := os.Remove(filepath.Join(fs.path, fsGenOldName(fd))); !os.IsNotExist(e1) {
   529  				fs.log(fmt.Sprintf("remove %s: %v (old name)", fd, err))
   530  				err = e1
   531  			}
   532  		} else {
   533  			fs.log(fmt.Sprintf("remove %s: %v", fd, err))
   534  		}
   535  	}
   536  	return err
   537  }
   538  
   539  func (fs *fileStorage) Rename(oldfd, newfd FileDesc) error {
   540  	if !FileDescOk(oldfd) || !FileDescOk(newfd) {
   541  		return ErrInvalidFile
   542  	}
   543  	if oldfd == newfd {
   544  		return nil
   545  	}
   546  	if fs.readOnly {
   547  		return errReadOnly
   548  	}
   549  
   550  	fs.mu.Lock()
   551  	defer fs.mu.Unlock()
   552  	if fs.open < 0 {
   553  		return ErrClosed
   554  	}
   555  	return rename(filepath.Join(fs.path, fsGenName(oldfd)), filepath.Join(fs.path, fsGenName(newfd)))
   556  }
   557  
   558  func (fs *fileStorage) Close() error {
   559  	fs.mu.Lock()
   560  	defer fs.mu.Unlock()
   561  	if fs.open < 0 {
   562  		return ErrClosed
   563  	}
   564  	// Clear the finalizer.
   565  	runtime.SetFinalizer(fs, nil)
   566  
   567  	if fs.open > 0 {
   568  		fs.log(fmt.Sprintf("close: warning, %d files still open", fs.open))
   569  	}
   570  	fs.open = -1
   571  	if fs.logw != nil {
   572  		fs.logw.Close()
   573  	}
   574  	return fs.flock.release()
   575  }
   576  
   577  type fileWrap struct {
   578  	*os.File
   579  	fs     *fileStorage
   580  	fd     FileDesc
   581  	closed bool
   582  }
   583  
   584  func (fw *fileWrap) Sync() error {
   585  	if err := fw.File.Sync(); err != nil {
   586  		return err
   587  	}
   588  	if fw.fd.Type == TypeManifest {
   589  		// Also sync parent directory if file type is manifest.
   590  		// See: https://code.google.com/p/leveldb/issues/detail?id=190.
   591  		if err := syncDir(fw.fs.path); err != nil {
   592  			fw.fs.log(fmt.Sprintf("syncDir: %v", err))
   593  			return err
   594  		}
   595  	}
   596  	return nil
   597  }
   598  
   599  func (fw *fileWrap) Close() error {
   600  	fw.fs.mu.Lock()
   601  	defer fw.fs.mu.Unlock()
   602  	if fw.closed {
   603  		return ErrClosed
   604  	}
   605  	fw.closed = true
   606  	fw.fs.open--
   607  	err := fw.File.Close()
   608  	if err != nil {
   609  		fw.fs.log(fmt.Sprintf("close %s: %v", fw.fd, err))
   610  	}
   611  	return err
   612  }
   613  
   614  func fsGenName(fd FileDesc) string {
   615  	switch fd.Type {
   616  	case TypeManifest:
   617  		return fmt.Sprintf("MANIFEST-%06d", fd.Num)
   618  	case TypeJournal:
   619  		return fmt.Sprintf("%06d.log", fd.Num)
   620  	case TypeTable:
   621  		return fmt.Sprintf("%06d.ldb", fd.Num)
   622  	case TypeTemp:
   623  		return fmt.Sprintf("%06d.tmp", fd.Num)
   624  	default:
   625  		panic("invalid file type")
   626  	}
   627  }
   628  
   629  func fsHasOldName(fd FileDesc) bool {
   630  	return fd.Type == TypeTable
   631  }
   632  
   633  func fsGenOldName(fd FileDesc) string {
   634  	switch fd.Type {
   635  	case TypeTable:
   636  		return fmt.Sprintf("%06d.sst", fd.Num)
   637  	}
   638  	return fsGenName(fd)
   639  }
   640  
   641  func fsParseName(name string) (fd FileDesc, ok bool) {
   642  	var tail string
   643  	_, err := fmt.Sscanf(name, "%d.%s", &fd.Num, &tail)
   644  	if err == nil {
   645  		switch tail {
   646  		case "log":
   647  			fd.Type = TypeJournal
   648  		case "ldb", "sst":
   649  			fd.Type = TypeTable
   650  		case "tmp":
   651  			fd.Type = TypeTemp
   652  		default:
   653  			return
   654  		}
   655  		return fd, true
   656  	}
   657  	n, _ := fmt.Sscanf(name, "MANIFEST-%d%s", &fd.Num, &tail)
   658  	if n == 1 {
   659  		fd.Type = TypeManifest
   660  		return fd, true
   661  	}
   662  	return
   663  }
   664  
   665  func fsParseNamePtr(name string, fd *FileDesc) bool {
   666  	_fd, ok := fsParseName(name)
   667  	if fd != nil {
   668  		*fd = _fd
   669  	}
   670  	return ok
   671  }