github.com/cs3org/reva/v2@v2.27.7/internal/http/services/archiver/manager/archiver.go (about) 1 // Copyright 2018-2021 CERN 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 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package manager 20 21 import ( 22 "archive/tar" 23 "archive/zip" 24 "context" 25 "io" 26 "path/filepath" 27 "time" 28 29 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 30 "github.com/cs3org/reva/v2/pkg/storage/utils/downloader" 31 "github.com/cs3org/reva/v2/pkg/storage/utils/walker" 32 "github.com/cs3org/reva/v2/pkg/utils" 33 ) 34 35 // Config is the config for the Archiver 36 type Config struct { 37 MaxNumFiles int64 38 MaxSize int64 39 } 40 41 // Archiver is the struct able to create an archive 42 type Archiver struct { 43 resources []*provider.ResourceId 44 walker walker.Walker 45 downloader downloader.Downloader 46 config Config 47 } 48 49 // NewArchiver creates a new archiver able to create an archive containing the files in the list 50 func NewArchiver(r []*provider.ResourceId, w walker.Walker, d downloader.Downloader, config Config) (*Archiver, error) { 51 if len(r) == 0 { 52 return nil, ErrEmptyList{} 53 } 54 55 arc := &Archiver{ 56 resources: r, 57 walker: w, 58 downloader: d, 59 config: config, 60 } 61 return arc, nil 62 } 63 64 // CreateTar creates a tar and write it into the dst Writer 65 func (a *Archiver) CreateTar(ctx context.Context, dst io.Writer) (func(), error) { 66 w := tar.NewWriter(dst) 67 closer := func() { 68 _ = w.Close() 69 } 70 71 var filesCount, sizeFiles int64 72 73 for _, root := range a.resources { 74 75 err := a.walker.Walk(ctx, root, func(wd string, info *provider.ResourceInfo, err error) error { 76 if err != nil { 77 return err 78 } 79 80 // when archiving a space we can omit the spaceroot 81 if utils.IsSpaceRoot(info) { 82 return nil 83 } 84 85 isDir := info.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER 86 87 filesCount++ 88 if filesCount > a.config.MaxNumFiles { 89 return ErrMaxFileCount{} 90 } 91 92 if !isDir { 93 // only add the size if the resource is not a directory 94 // as its size could be resursive-computed, and we would 95 // count the files not only once 96 sizeFiles += int64(info.Size) 97 if sizeFiles > a.config.MaxSize { 98 return ErrMaxSize{} 99 } 100 } 101 102 header := tar.Header{ 103 Name: filepath.Join(wd, info.Path), 104 ModTime: time.Unix(int64(info.Mtime.Seconds), 0), 105 } 106 107 if isDir { 108 // the resource is a folder 109 header.Mode = 0755 110 header.Typeflag = tar.TypeDir 111 } else { 112 header.Mode = 0644 113 header.Typeflag = tar.TypeReg 114 header.Size = int64(info.Size) 115 } 116 117 err = w.WriteHeader(&header) 118 if err != nil { 119 return err 120 } 121 122 if !isDir { 123 err = a.downloader.Download(ctx, info.Id, w) 124 if err != nil { 125 return err 126 } 127 } 128 return nil 129 }) 130 131 if err != nil { 132 return closer, err 133 } 134 135 } 136 return closer, nil 137 } 138 139 // CreateZip creates a zip and write it into the dst Writer 140 func (a *Archiver) CreateZip(ctx context.Context, dst io.Writer) (func(), error) { 141 w := zip.NewWriter(dst) 142 closer := func() { 143 _ = w.Close() 144 } 145 146 var filesCount, sizeFiles int64 147 148 for _, root := range a.resources { 149 150 err := a.walker.Walk(ctx, root, func(wd string, info *provider.ResourceInfo, err error) error { 151 if err != nil { 152 return err 153 } 154 155 // when archiving a space we can omit the spaceroot 156 if utils.IsSpaceRoot(info) { 157 return nil 158 } 159 160 isDir := info.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER 161 162 filesCount++ 163 if filesCount > a.config.MaxNumFiles { 164 return ErrMaxFileCount{} 165 } 166 167 if !isDir { 168 // only add the size if the resource is not a directory 169 // as its size could be resursive-computed, and we would 170 // count the files not only once 171 sizeFiles += int64(info.Size) 172 if sizeFiles > a.config.MaxSize { 173 return ErrMaxSize{} 174 } 175 } 176 177 header := zip.FileHeader{ 178 Name: filepath.Join(wd, info.Path), 179 Modified: time.Unix(int64(info.Mtime.Seconds), 0), 180 } 181 182 if isDir { 183 header.Name += "/" 184 } else { 185 header.UncompressedSize64 = info.Size 186 } 187 188 dst, err := w.CreateHeader(&header) 189 if err != nil { 190 return err 191 } 192 193 if !isDir { 194 err = a.downloader.Download(ctx, info.Id, dst) 195 if err != nil { 196 return err 197 } 198 } 199 return nil 200 }) 201 202 if err != nil { 203 return closer, err 204 } 205 206 } 207 return closer, nil 208 }