1// Copyright 2020 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// Package embed provides access to files embedded in the running Go program. 6// 7// Go source files that import "embed" can use the //go:embed directive 8// to initialize a variable of type string, []byte, or [FS] with the contents of 9// files read from the package directory or subdirectories at compile time. 10// 11// For example, here are three ways to embed a file named hello.txt 12// and then print its contents at run time. 13// 14// Embedding one file into a string: 15// 16// import _ "embed" 17// 18// //go:embed hello.txt 19// var s string 20// print(s) 21// 22// Embedding one file into a slice of bytes: 23// 24// import _ "embed" 25// 26// //go:embed hello.txt 27// var b []byte 28// print(string(b)) 29// 30// Embedded one or more files into a file system: 31// 32// import "embed" 33// 34// //go:embed hello.txt 35// var f embed.FS 36// data, _ := f.ReadFile("hello.txt") 37// print(string(data)) 38// 39// # Directives 40// 41// A //go:embed directive above a variable declaration specifies which files to embed, 42// using one or more path.Match patterns. 43// 44// The directive must immediately precede a line containing the declaration of a single variable. 45// Only blank lines and ‘//’ line comments are permitted between the directive and the declaration. 46// 47// The type of the variable must be a string type, or a slice of a byte type, 48// or [FS] (or an alias of [FS]). 49// 50// For example: 51// 52// package server 53// 54// import "embed" 55// 56// // content holds our static web server content. 57// //go:embed image/* template/* 58// //go:embed html/index.html 59// var content embed.FS 60// 61// The Go build system will recognize the directives and arrange for the declared variable 62// (in the example above, content) to be populated with the matching files from the file system. 63// 64// The //go:embed directive accepts multiple space-separated patterns for 65// brevity, but it can also be repeated, to avoid very long lines when there are 66// many patterns. The patterns are interpreted relative to the package directory 67// containing the source file. The path separator is a forward slash, even on 68// Windows systems. Patterns may not contain ‘.’ or ‘..’ or empty path elements, 69// nor may they begin or end with a slash. To match everything in the current 70// directory, use ‘*’ instead of ‘.’. To allow for naming files with spaces in 71// their names, patterns can be written as Go double-quoted or back-quoted 72// string literals. 73// 74// If a pattern names a directory, all files in the subtree rooted at that directory are 75// embedded (recursively), except that files with names beginning with ‘.’ or ‘_’ 76// are excluded. So the variable in the above example is almost equivalent to: 77// 78// // content is our static web server content. 79// //go:embed image template html/index.html 80// var content embed.FS 81// 82// The difference is that ‘image/*’ embeds ‘image/.tempfile’ while ‘image’ does not. 83// Neither embeds ‘image/dir/.tempfile’. 84// 85// If a pattern begins with the prefix ‘all:’, then the rule for walking directories is changed 86// to include those files beginning with ‘.’ or ‘_’. For example, ‘all:image’ embeds 87// both ‘image/.tempfile’ and ‘image/dir/.tempfile’. 88// 89// The //go:embed directive can be used with both exported and unexported variables, 90// depending on whether the package wants to make the data available to other packages. 91// It can only be used with variables at package scope, not with local variables. 92// 93// Patterns must not match files outside the package's module, such as ‘.git/*’ or symbolic links. 94// Patterns must not match files whose names include the special punctuation characters " * < > ? ` ' | / \ and :. 95// Matches for empty directories are ignored. After that, each pattern in a //go:embed line 96// must match at least one file or non-empty directory. 97// 98// If any patterns are invalid or have invalid matches, the build will fail. 99// 100// # Strings and Bytes 101// 102// The //go:embed line for a variable of type string or []byte can have only a single pattern, 103// and that pattern can match only a single file. The string or []byte is initialized with 104// the contents of that file. 105// 106// The //go:embed directive requires importing "embed", even when using a string or []byte. 107// In source files that don't refer to [embed.FS], use a blank import (import _ "embed"). 108// 109// # File Systems 110// 111// For embedding a single file, a variable of type string or []byte is often best. 112// The [FS] type enables embedding a tree of files, such as a directory of static 113// web server content, as in the example above. 114// 115// FS implements the [io/fs] package's [FS] interface, so it can be used with any package that 116// understands file systems, including [net/http], [text/template], and [html/template]. 117// 118// For example, given the content variable in the example above, we can write: 119// 120// http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(content)))) 121// 122// template.ParseFS(content, "*.tmpl") 123// 124// # Tools 125// 126// To support tools that analyze Go packages, the patterns found in //go:embed lines 127// are available in “go list” output. See the EmbedPatterns, TestEmbedPatterns, 128// and XTestEmbedPatterns fields in the “go help list” output. 129package embed 130 131import ( 132 "errors" 133 "internal/bytealg" 134 "internal/stringslite" 135 "io" 136 "io/fs" 137 "time" 138) 139 140// An FS is a read-only collection of files, usually initialized with a //go:embed directive. 141// When declared without a //go:embed directive, an FS is an empty file system. 142// 143// An FS is a read-only value, so it is safe to use from multiple goroutines 144// simultaneously and also safe to assign values of type FS to each other. 145// 146// FS implements fs.FS, so it can be used with any package that understands 147// file system interfaces, including net/http, text/template, and html/template. 148// 149// See the package documentation for more details about initializing an FS. 150type FS struct { 151 // The compiler knows the layout of this struct. 152 // See cmd/compile/internal/staticdata's WriteEmbed. 153 // 154 // The files list is sorted by name but not by simple string comparison. 155 // Instead, each file's name takes the form "dir/elem" or "dir/elem/". 156 // The optional trailing slash indicates that the file is itself a directory. 157 // The files list is sorted first by dir (if dir is missing, it is taken to be ".") 158 // and then by base, so this list of files: 159 // 160 // p 161 // q/ 162 // q/r 163 // q/s/ 164 // q/s/t 165 // q/s/u 166 // q/v 167 // w 168 // 169 // is actually sorted as: 170 // 171 // p # dir=. elem=p 172 // q/ # dir=. elem=q 173 // w/ # dir=. elem=w 174 // q/r # dir=q elem=r 175 // q/s/ # dir=q elem=s 176 // q/v # dir=q elem=v 177 // q/s/t # dir=q/s elem=t 178 // q/s/u # dir=q/s elem=u 179 // 180 // This order brings directory contents together in contiguous sections 181 // of the list, allowing a directory read to use binary search to find 182 // the relevant sequence of entries. 183 files *[]file 184} 185 186// split splits the name into dir and elem as described in the 187// comment in the FS struct above. isDir reports whether the 188// final trailing slash was present, indicating that name is a directory. 189func split(name string) (dir, elem string, isDir bool) { 190 name, isDir = stringslite.CutSuffix(name, "/") 191 i := bytealg.LastIndexByteString(name, '/') 192 if i < 0 { 193 return ".", name, isDir 194 } 195 return name[:i], name[i+1:], isDir 196} 197 198var ( 199 _ fs.ReadDirFS = FS{} 200 _ fs.ReadFileFS = FS{} 201) 202 203// A file is a single file in the FS. 204// It implements fs.FileInfo and fs.DirEntry. 205type file struct { 206 // The compiler knows the layout of this struct. 207 // See cmd/compile/internal/staticdata's WriteEmbed. 208 name string 209 data string 210 hash [16]byte // truncated SHA256 hash 211} 212 213var ( 214 _ fs.FileInfo = (*file)(nil) 215 _ fs.DirEntry = (*file)(nil) 216) 217 218func (f *file) Name() string { _, elem, _ := split(f.name); return elem } 219func (f *file) Size() int64 { return int64(len(f.data)) } 220func (f *file) ModTime() time.Time { return time.Time{} } 221func (f *file) IsDir() bool { _, _, isDir := split(f.name); return isDir } 222func (f *file) Sys() any { return nil } 223func (f *file) Type() fs.FileMode { return f.Mode().Type() } 224func (f *file) Info() (fs.FileInfo, error) { return f, nil } 225 226func (f *file) Mode() fs.FileMode { 227 if f.IsDir() { 228 return fs.ModeDir | 0555 229 } 230 return 0444 231} 232 233func (f *file) String() string { 234 return fs.FormatFileInfo(f) 235} 236 237// dotFile is a file for the root directory, 238// which is omitted from the files list in a FS. 239var dotFile = &file{name: "./"} 240 241// lookup returns the named file, or nil if it is not present. 242func (f FS) lookup(name string) *file { 243 if !fs.ValidPath(name) { 244 // The compiler should never emit a file with an invalid name, 245 // so this check is not strictly necessary (if name is invalid, 246 // we shouldn't find a match below), but it's a good backstop anyway. 247 return nil 248 } 249 if name == "." { 250 return dotFile 251 } 252 if f.files == nil { 253 return nil 254 } 255 256 // Binary search to find where name would be in the list, 257 // and then check if name is at that position. 258 dir, elem, _ := split(name) 259 files := *f.files 260 i := sortSearch(len(files), func(i int) bool { 261 idir, ielem, _ := split(files[i].name) 262 return idir > dir || idir == dir && ielem >= elem 263 }) 264 if i < len(files) && stringslite.TrimSuffix(files[i].name, "/") == name { 265 return &files[i] 266 } 267 return nil 268} 269 270// readDir returns the list of files corresponding to the directory dir. 271func (f FS) readDir(dir string) []file { 272 if f.files == nil { 273 return nil 274 } 275 // Binary search to find where dir starts and ends in the list 276 // and then return that slice of the list. 277 files := *f.files 278 i := sortSearch(len(files), func(i int) bool { 279 idir, _, _ := split(files[i].name) 280 return idir >= dir 281 }) 282 j := sortSearch(len(files), func(j int) bool { 283 jdir, _, _ := split(files[j].name) 284 return jdir > dir 285 }) 286 return files[i:j] 287} 288 289// Open opens the named file for reading and returns it as an [fs.File]. 290// 291// The returned file implements [io.Seeker] and [io.ReaderAt] when the file is not a directory. 292func (f FS) Open(name string) (fs.File, error) { 293 file := f.lookup(name) 294 if file == nil { 295 return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} 296 } 297 if file.IsDir() { 298 return &openDir{file, f.readDir(name), 0}, nil 299 } 300 return &openFile{file, 0}, nil 301} 302 303// ReadDir reads and returns the entire named directory. 304func (f FS) ReadDir(name string) ([]fs.DirEntry, error) { 305 file, err := f.Open(name) 306 if err != nil { 307 return nil, err 308 } 309 dir, ok := file.(*openDir) 310 if !ok { 311 return nil, &fs.PathError{Op: "read", Path: name, Err: errors.New("not a directory")} 312 } 313 list := make([]fs.DirEntry, len(dir.files)) 314 for i := range list { 315 list[i] = &dir.files[i] 316 } 317 return list, nil 318} 319 320// ReadFile reads and returns the content of the named file. 321func (f FS) ReadFile(name string) ([]byte, error) { 322 file, err := f.Open(name) 323 if err != nil { 324 return nil, err 325 } 326 ofile, ok := file.(*openFile) 327 if !ok { 328 return nil, &fs.PathError{Op: "read", Path: name, Err: errors.New("is a directory")} 329 } 330 return []byte(ofile.f.data), nil 331} 332 333// An openFile is a regular file open for reading. 334type openFile struct { 335 f *file // the file itself 336 offset int64 // current read offset 337} 338 339var ( 340 _ io.Seeker = (*openFile)(nil) 341 _ io.ReaderAt = (*openFile)(nil) 342) 343 344func (f *openFile) Close() error { return nil } 345func (f *openFile) Stat() (fs.FileInfo, error) { return f.f, nil } 346 347func (f *openFile) Read(b []byte) (int, error) { 348 if f.offset >= int64(len(f.f.data)) { 349 return 0, io.EOF 350 } 351 if f.offset < 0 { 352 return 0, &fs.PathError{Op: "read", Path: f.f.name, Err: fs.ErrInvalid} 353 } 354 n := copy(b, f.f.data[f.offset:]) 355 f.offset += int64(n) 356 return n, nil 357} 358 359func (f *openFile) Seek(offset int64, whence int) (int64, error) { 360 switch whence { 361 case 0: 362 // offset += 0 363 case 1: 364 offset += f.offset 365 case 2: 366 offset += int64(len(f.f.data)) 367 } 368 if offset < 0 || offset > int64(len(f.f.data)) { 369 return 0, &fs.PathError{Op: "seek", Path: f.f.name, Err: fs.ErrInvalid} 370 } 371 f.offset = offset 372 return offset, nil 373} 374 375func (f *openFile) ReadAt(b []byte, offset int64) (int, error) { 376 if offset < 0 || offset > int64(len(f.f.data)) { 377 return 0, &fs.PathError{Op: "read", Path: f.f.name, Err: fs.ErrInvalid} 378 } 379 n := copy(b, f.f.data[offset:]) 380 if n < len(b) { 381 return n, io.EOF 382 } 383 return n, nil 384} 385 386// An openDir is a directory open for reading. 387type openDir struct { 388 f *file // the directory file itself 389 files []file // the directory contents 390 offset int // the read offset, an index into the files slice 391} 392 393func (d *openDir) Close() error { return nil } 394func (d *openDir) Stat() (fs.FileInfo, error) { return d.f, nil } 395 396func (d *openDir) Read([]byte) (int, error) { 397 return 0, &fs.PathError{Op: "read", Path: d.f.name, Err: errors.New("is a directory")} 398} 399 400func (d *openDir) ReadDir(count int) ([]fs.DirEntry, error) { 401 n := len(d.files) - d.offset 402 if n == 0 { 403 if count <= 0 { 404 return nil, nil 405 } 406 return nil, io.EOF 407 } 408 if count > 0 && n > count { 409 n = count 410 } 411 list := make([]fs.DirEntry, n) 412 for i := range list { 413 list[i] = &d.files[d.offset+i] 414 } 415 d.offset += n 416 return list, nil 417} 418 419// sortSearch is like sort.Search, avoiding an import. 420func sortSearch(n int, f func(int) bool) int { 421 // Define f(-1) == false and f(n) == true. 422 // Invariant: f(i-1) == false, f(j) == true. 423 i, j := 0, n 424 for i < j { 425 h := int(uint(i+j) >> 1) // avoid overflow when computing h 426 // i ≤ h < j 427 if !f(h) { 428 i = h + 1 // preserves f(i-1) == false 429 } else { 430 j = h // preserves f(j) == true 431 } 432 } 433 // i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i. 434 return i 435} 436