hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

nosymlink_fs.go (3905B)

    1 // Copyright 2018 The Hugo Authors. All rights reserved.
    2 //
    3 // Licensed under the Apache License, Version 2.0 (the "License");
    4 // you may not use this file except in compliance with the License.
    5 // You may obtain a copy of the License at
    6 // http://www.apache.org/licenses/LICENSE-2.0
    7 //
    8 // Unless required by applicable law or agreed to in writing, software
    9 // distributed under the License is distributed on an "AS IS" BASIS,
   10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   11 // See the License for the specific language governing permissions and
   12 // limitations under the License.
   13 
   14 package hugofs
   15 
   16 import (
   17 	"errors"
   18 	"os"
   19 	"path/filepath"
   20 
   21 	"github.com/gohugoio/hugo/common/loggers"
   22 
   23 	"github.com/spf13/afero"
   24 )
   25 
   26 var ErrPermissionSymlink = errors.New("symlinks not allowed in this filesystem")
   27 
   28 // NewNoSymlinkFs creates a new filesystem that prevents symlinks.
   29 func NewNoSymlinkFs(fs afero.Fs, logger loggers.Logger, allowFiles bool) afero.Fs {
   30 	return &noSymlinkFs{Fs: fs, logger: logger, allowFiles: allowFiles}
   31 }
   32 
   33 var (
   34 	_ FilesystemUnwrapper = (*noSymlinkFs)(nil)
   35 )
   36 
   37 // noSymlinkFs is a filesystem that prevents symlinking.
   38 type noSymlinkFs struct {
   39 	allowFiles bool // block dirs only
   40 	logger     loggers.Logger
   41 	afero.Fs
   42 }
   43 
   44 type noSymlinkFile struct {
   45 	fs *noSymlinkFs
   46 	afero.File
   47 }
   48 
   49 func (f *noSymlinkFile) Readdir(count int) ([]os.FileInfo, error) {
   50 	fis, err := f.File.Readdir(count)
   51 
   52 	filtered := fis[:0]
   53 	for _, x := range fis {
   54 		filename := filepath.Join(f.Name(), x.Name())
   55 		if _, err := f.fs.checkSymlinkStatus(filename, x); err != nil {
   56 			// Log a warning and drop the file from the list
   57 			logUnsupportedSymlink(filename, f.fs.logger)
   58 		} else {
   59 			filtered = append(filtered, x)
   60 		}
   61 	}
   62 
   63 	return filtered, err
   64 }
   65 
   66 func (f *noSymlinkFile) Readdirnames(count int) ([]string, error) {
   67 	dirs, err := f.Readdir(count)
   68 	if err != nil {
   69 		return nil, err
   70 	}
   71 	return fileInfosToNames(dirs), nil
   72 }
   73 
   74 func (fs *noSymlinkFs) UnwrapFilesystem() afero.Fs {
   75 	return fs.Fs
   76 }
   77 
   78 func (fs *noSymlinkFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
   79 	return fs.stat(name)
   80 }
   81 
   82 func (fs *noSymlinkFs) Stat(name string) (os.FileInfo, error) {
   83 	fi, _, err := fs.stat(name)
   84 	return fi, err
   85 }
   86 
   87 func (fs *noSymlinkFs) stat(name string) (os.FileInfo, bool, error) {
   88 	var (
   89 		fi       os.FileInfo
   90 		wasLstat bool
   91 		err      error
   92 	)
   93 
   94 	if lstater, ok := fs.Fs.(afero.Lstater); ok {
   95 		fi, wasLstat, err = lstater.LstatIfPossible(name)
   96 	} else {
   97 		fi, err = fs.Fs.Stat(name)
   98 	}
   99 
  100 	if err != nil {
  101 		return nil, false, err
  102 	}
  103 
  104 	fi, err = fs.checkSymlinkStatus(name, fi)
  105 
  106 	return fi, wasLstat, err
  107 }
  108 
  109 func (fs *noSymlinkFs) checkSymlinkStatus(name string, fi os.FileInfo) (os.FileInfo, error) {
  110 	var metaIsSymlink bool
  111 
  112 	if fim, ok := fi.(FileMetaInfo); ok {
  113 		meta := fim.Meta()
  114 		metaIsSymlink = meta.IsSymlink
  115 	}
  116 
  117 	if metaIsSymlink {
  118 		if fs.allowFiles && !fi.IsDir() {
  119 			return fi, nil
  120 		}
  121 		return nil, ErrPermissionSymlink
  122 	}
  123 
  124 	// Also support non-decorated filesystems, e.g. the Os fs.
  125 	if isSymlink(fi) {
  126 		// Need to determine if this is a directory or not.
  127 		_, sfi, err := evalSymlinks(fs.Fs, name)
  128 		if err != nil {
  129 			return nil, err
  130 		}
  131 		if fs.allowFiles && !sfi.IsDir() {
  132 			// Return the original FileInfo to get the expected Name.
  133 			return fi, nil
  134 		}
  135 		return nil, ErrPermissionSymlink
  136 	}
  137 
  138 	return fi, nil
  139 }
  140 
  141 func (fs *noSymlinkFs) Open(name string) (afero.File, error) {
  142 	if _, _, err := fs.stat(name); err != nil {
  143 		return nil, err
  144 	}
  145 	return fs.wrapFile(fs.Fs.Open(name))
  146 }
  147 
  148 func (fs *noSymlinkFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) {
  149 	if _, _, err := fs.stat(name); err != nil {
  150 		return nil, err
  151 	}
  152 	return fs.wrapFile(fs.Fs.OpenFile(name, flag, perm))
  153 }
  154 
  155 func (fs *noSymlinkFs) wrapFile(f afero.File, err error) (afero.File, error) {
  156 	if err != nil {
  157 		return nil, err
  158 	}
  159 
  160 	return &noSymlinkFile{File: f, fs: fs}, nil
  161 }