1// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2// See LICENSE.txt for license information. 3 4package filestore 5 6import ( 7 "bytes" 8 "io" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 "time" 13 14 "github.com/pkg/errors" 15 16 "github.com/mattermost/mattermost-server/v6/shared/mlog" 17) 18 19const ( 20 TestFilePath = "/testfile" 21) 22 23type LocalFileBackend struct { 24 directory string 25} 26 27// copyFile will copy a file from src path to dst path. 28// Overwrites any existing files at dst. 29// Permissions are copied from file at src to the new file at dst. 30func copyFile(src, dst string) (err error) { 31 in, err := os.Open(src) 32 if err != nil { 33 return 34 } 35 defer in.Close() 36 37 if err = os.MkdirAll(filepath.Dir(dst), os.ModePerm); err != nil { 38 return 39 } 40 out, err := os.Create(dst) 41 if err != nil { 42 return 43 } 44 defer func() { 45 if e := out.Close(); e != nil { 46 err = e 47 } 48 }() 49 50 _, err = io.Copy(out, in) 51 if err != nil { 52 return 53 } 54 55 err = out.Sync() 56 if err != nil { 57 return 58 } 59 60 stat, err := os.Stat(src) 61 if err != nil { 62 return 63 } 64 err = os.Chmod(dst, stat.Mode()) 65 if err != nil { 66 return 67 } 68 69 return 70} 71 72func (b *LocalFileBackend) TestConnection() error { 73 f := bytes.NewReader([]byte("testingwrite")) 74 if _, err := writeFileLocally(f, filepath.Join(b.directory, TestFilePath)); err != nil { 75 return errors.Wrap(err, "unable to write to the local filesystem storage") 76 } 77 os.Remove(filepath.Join(b.directory, TestFilePath)) 78 mlog.Debug("Able to write files to local storage.") 79 return nil 80} 81 82func (b *LocalFileBackend) Reader(path string) (ReadCloseSeeker, error) { 83 f, err := os.Open(filepath.Join(b.directory, path)) 84 if err != nil { 85 return nil, errors.Wrapf(err, "unable to open file %s", path) 86 } 87 return f, nil 88} 89 90func (b *LocalFileBackend) ReadFile(path string) ([]byte, error) { 91 f, err := ioutil.ReadFile(filepath.Join(b.directory, path)) 92 if err != nil { 93 return nil, errors.Wrapf(err, "unable to read file %s", path) 94 } 95 return f, nil 96} 97 98func (b *LocalFileBackend) FileExists(path string) (bool, error) { 99 _, err := os.Stat(filepath.Join(b.directory, path)) 100 101 if os.IsNotExist(err) { 102 return false, nil 103 } 104 105 if err != nil { 106 return false, errors.Wrapf(err, "unable to know if file %s exists", path) 107 } 108 return true, nil 109} 110 111func (b *LocalFileBackend) FileSize(path string) (int64, error) { 112 info, err := os.Stat(filepath.Join(b.directory, path)) 113 if err != nil { 114 return 0, errors.Wrapf(err, "unable to get file size for %s", path) 115 } 116 return info.Size(), nil 117} 118 119func (b *LocalFileBackend) FileModTime(path string) (time.Time, error) { 120 info, err := os.Stat(filepath.Join(b.directory, path)) 121 if err != nil { 122 return time.Time{}, errors.Wrapf(err, "unable to get modification time for file %s", path) 123 } 124 return info.ModTime(), nil 125} 126 127func (b *LocalFileBackend) CopyFile(oldPath, newPath string) error { 128 if err := copyFile(filepath.Join(b.directory, oldPath), filepath.Join(b.directory, newPath)); err != nil { 129 return errors.Wrapf(err, "unable to copy file from %s to %s", oldPath, newPath) 130 } 131 return nil 132} 133 134func (b *LocalFileBackend) MoveFile(oldPath, newPath string) error { 135 if err := os.MkdirAll(filepath.Dir(filepath.Join(b.directory, newPath)), 0750); err != nil { 136 return errors.Wrapf(err, "unable to create the new destination directory %s", filepath.Dir(newPath)) 137 } 138 139 if err := os.Rename(filepath.Join(b.directory, oldPath), filepath.Join(b.directory, newPath)); err != nil { 140 return errors.Wrapf(err, "unable to move the file to %s to the destination directory", newPath) 141 } 142 143 return nil 144} 145 146func (b *LocalFileBackend) WriteFile(fr io.Reader, path string) (int64, error) { 147 return writeFileLocally(fr, filepath.Join(b.directory, path)) 148} 149 150func writeFileLocally(fr io.Reader, path string) (int64, error) { 151 if err := os.MkdirAll(filepath.Dir(path), 0750); err != nil { 152 directory, _ := filepath.Abs(filepath.Dir(path)) 153 return 0, errors.Wrapf(err, "unable to create the directory %s for the file %s", directory, path) 154 } 155 fw, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) 156 if err != nil { 157 return 0, errors.Wrapf(err, "unable to open the file %s to write the data", path) 158 } 159 defer fw.Close() 160 written, err := io.Copy(fw, fr) 161 if err != nil { 162 return written, errors.Wrapf(err, "unable write the data in the file %s", path) 163 } 164 return written, nil 165} 166 167func (b *LocalFileBackend) AppendFile(fr io.Reader, path string) (int64, error) { 168 fp := filepath.Join(b.directory, path) 169 if _, err := os.Stat(fp); err != nil { 170 return 0, errors.Wrapf(err, "unable to find the file %s to append the data", path) 171 } 172 fw, err := os.OpenFile(fp, os.O_WRONLY|os.O_APPEND, 0600) 173 if err != nil { 174 return 0, errors.Wrapf(err, "unable to open the file %s to append the data", path) 175 } 176 defer fw.Close() 177 written, err := io.Copy(fw, fr) 178 if err != nil { 179 return written, errors.Wrapf(err, "unable append the data in the file %s", path) 180 } 181 return written, nil 182} 183 184func (b *LocalFileBackend) RemoveFile(path string) error { 185 if err := os.Remove(filepath.Join(b.directory, path)); err != nil { 186 return errors.Wrapf(err, "unable to remove the file %s", path) 187 } 188 return nil 189} 190 191func (b *LocalFileBackend) ListDirectory(path string) ([]string, error) { 192 var paths []string 193 fileInfos, err := ioutil.ReadDir(filepath.Join(b.directory, path)) 194 if err != nil { 195 if os.IsNotExist(err) { 196 return paths, nil 197 } 198 return nil, errors.Wrapf(err, "unable to list the directory %s", path) 199 } 200 for _, fileInfo := range fileInfos { 201 paths = append(paths, filepath.Join(path, fileInfo.Name())) 202 } 203 return paths, nil 204} 205 206func (b *LocalFileBackend) RemoveDirectory(path string) error { 207 if err := os.RemoveAll(filepath.Join(b.directory, path)); err != nil { 208 return errors.Wrapf(err, "unable to remove the directory %s", path) 209 } 210 return nil 211} 212