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