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/*’, symbolic links,
94 // 'vendor/', or any directories containing go.mod (these are separate modules).
95 // Patterns must not match files whose names include the special punctuation characters " * < > ? ` ' | / \ and :.
96 // Matches for empty directories are ignored. After that, each pattern in a //go:embed line
97 // must match at least one file or non-empty directory.
98 //
99 // If any patterns are invalid or have invalid matches, the build will fail.
100 //
101 // # Strings and Bytes
102 //
103 // The //go:embed line for a variable of type string or []byte can have only a single pattern,
104 // and that pattern can match only a single file. The string or []byte is initialized with
105 // the contents of that file.
106 //
107 // The //go:embed directive requires importing "embed", even when using a string or []byte.
108 // In source files that don't refer to [embed.FS], use a blank import (import _ "embed").
109 //
110 // # File Systems
111 //
112 // For embedding a single file, a variable of type string or []byte is often best.
113 // The [FS] type enables embedding a tree of files, such as a directory of static
114 // web server content, as in the example above.
115 //
116 // FS implements the [io/fs] package's [FS] interface, so it can be used with any package that
117 // understands file systems, including [net/http], [text/template], and [html/template].
118 //
119 // For example, given the content variable in the example above, we can write:
120 //
121 // http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(content))))
122 //
123 // template.ParseFS(content, "*.tmpl")
124 //
125 // # Tools
126 //
127 // To support tools that analyze Go packages, the patterns found in //go:embed lines
128 // are available in “go list” output. See the EmbedPatterns, TestEmbedPatterns,
129 // and XTestEmbedPatterns fields in the “go help list” output.
130 package embed
131 132 import (
133 "errors"
134 "internal/bytealg"
135 "internal/stringslite"
136 "io"
137 "io/fs"
138 "time"
139 )
140 141 // An FS is a read-only collection of files, usually initialized with a //go:embed directive.
142 // When declared without a //go:embed directive, an FS is an empty file system.
143 //
144 // An FS is a read-only value, so it is safe to use from multiple goroutines
145 // simultaneously and also safe to assign values of type FS to each other.
146 //
147 // FS implements fs.FS, so it can be used with any package that understands
148 // file system interfaces, including net/http, text/template, and html/template.
149 //
150 // See the package documentation for more details about initializing an FS.
151 type FS struct {
152 // The compiler knows the layout of this struct.
153 // See cmd/compile/internal/staticdata's WriteEmbed.
154 //
155 // The files list is sorted by name but not by simple string comparison.
156 // Instead, each file's name takes the form "dir/elem" or "dir/elem/".
157 // The optional trailing slash indicates that the file is itself a directory.
158 // The files list is sorted first by dir (if dir is missing, it is taken to be ".")
159 // and then by base, so this list of files:
160 //
161 // p
162 // q/
163 // q/r
164 // q/s/
165 // q/s/t
166 // q/s/u
167 // q/v
168 // w
169 //
170 // is actually sorted as:
171 //
172 // p # dir=. elem=p
173 // q/ # dir=. elem=q
174 // w # dir=. elem=w
175 // q/r # dir=q elem=r
176 // q/s/ # dir=q elem=s
177 // q/v # dir=q elem=v
178 // q/s/t # dir=q/s elem=t
179 // q/s/u # dir=q/s elem=u
180 //
181 // This order brings directory contents together in contiguous sections
182 // of the list, allowing a directory read to use binary search to find
183 // the relevant sequence of entries.
184 files *[]file
185 }
186 187 // split splits the name into dir and elem as described in the
188 // comment in the FS struct above. isDir reports whether the
189 // final trailing slash was present, indicating that name is a directory.
190 func split(name string) (dir, elem string, isDir bool) {
191 name, isDir = stringslite.CutSuffix(name, "/")
192 i := bytealg.LastIndexByteString(name, '/')
193 if i < 0 {
194 return ".", name, isDir
195 }
196 return name[:i], name[i+1:], isDir
197 }
198 199 var (
200 _ fs.ReadDirFS = FS{}
201 _ fs.ReadFileFS = FS{}
202 )
203 204 // A file is a single file in the FS.
205 // It implements fs.FileInfo and fs.DirEntry.
206 type file struct {
207 // The compiler knows the layout of this struct.
208 // See cmd/compile/internal/staticdata's WriteEmbed.
209 name string
210 data string
211 hash [16]byte // truncated SHA256 hash
212 }
213 214 var (
215 _ fs.FileInfo = (*file)(nil)
216 _ fs.DirEntry = (*file)(nil)
217 )
218 219 func (f *file) Name() string { _, elem, _ := split(f.name); return elem }
220 func (f *file) Size() int64 { return int64(len(f.data)) }
221 func (f *file) ModTime() time.Time { return time.Time{} }
222 func (f *file) IsDir() bool { _, _, isDir := split(f.name); return isDir }
223 func (f *file) Sys() any { return nil }
224 func (f *file) Type() fs.FileMode { return f.Mode().Type() }
225 func (f *file) Info() (fs.FileInfo, error) { return f, nil }
226 227 func (f *file) Mode() fs.FileMode {
228 if f.IsDir() {
229 return fs.ModeDir | 0555
230 }
231 return 0444
232 }
233 234 func (f *file) String() string {
235 return fs.FormatFileInfo(f)
236 }
237 238 // dotFile is a file for the root directory,
239 // which is omitted from the files list in a FS.
240 var dotFile = &file{name: "./"}
241 242 // lookup returns the named file, or nil if it is not present.
243 func (f FS) lookup(name string) *file {
244 if !fs.ValidPath(name) {
245 // The compiler should never emit a file with an invalid name,
246 // so this check is not strictly necessary (if name is invalid,
247 // we shouldn't find a match below), but it's a good backstop anyway.
248 return nil
249 }
250 if name == "." {
251 return dotFile
252 }
253 if f.files == nil {
254 return nil
255 }
256 257 // Binary search to find where name would be in the list,
258 // and then check if name is at that position.
259 dir, elem, _ := split(name)
260 files := *f.files
261 i := sortSearch(len(files), func(i int) bool {
262 idir, ielem, _ := split(files[i].name)
263 return idir > dir || idir == dir && ielem >= elem
264 })
265 if i < len(files) && stringslite.TrimSuffix(files[i].name, "/") == name {
266 return &files[i]
267 }
268 return nil
269 }
270 271 // readDir returns the list of files corresponding to the directory dir.
272 func (f FS) readDir(dir string) []file {
273 if f.files == nil {
274 return nil
275 }
276 // Binary search to find where dir starts and ends in the list
277 // and then return that slice of the list.
278 files := *f.files
279 i := sortSearch(len(files), func(i int) bool {
280 idir, _, _ := split(files[i].name)
281 return idir >= dir
282 })
283 j := sortSearch(len(files), func(j int) bool {
284 jdir, _, _ := split(files[j].name)
285 return jdir > dir
286 })
287 return files[i:j]
288 }
289 290 // Open opens the named file for reading and returns it as an [fs.File].
291 //
292 // The returned file implements [io.Seeker] and [io.ReaderAt] when the file is not a directory.
293 func (f FS) Open(name string) (fs.File, error) {
294 file := f.lookup(name)
295 if file == nil {
296 return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
297 }
298 if file.IsDir() {
299 return &openDir{file, f.readDir(name), 0}, nil
300 }
301 return &openFile{file, 0}, nil
302 }
303 304 // ReadDir reads and returns the entire named directory.
305 func (f FS) ReadDir(name string) ([]fs.DirEntry, error) {
306 file, err := f.Open(name)
307 if err != nil {
308 return nil, err
309 }
310 dir, ok := file.(*openDir)
311 if !ok {
312 return nil, &fs.PathError{Op: "read", Path: name, Err: errors.New("not a directory")}
313 }
314 list := []fs.DirEntry{:len(dir.files)}
315 for i := range list {
316 list[i] = &dir.files[i]
317 }
318 return list, nil
319 }
320 321 // ReadFile reads and returns the content of the named file.
322 func (f FS) ReadFile(name string) ([]byte, error) {
323 file, err := f.Open(name)
324 if err != nil {
325 return nil, err
326 }
327 ofile, ok := file.(*openFile)
328 if !ok {
329 return nil, &fs.PathError{Op: "read", Path: name, Err: errors.New("is a directory")}
330 }
331 return []byte(ofile.f.data), nil
332 }
333 334 // An openFile is a regular file open for reading.
335 type openFile struct {
336 f *file // the file itself
337 offset int64 // current read offset
338 }
339 340 var (
341 _ io.Seeker = (*openFile)(nil)
342 _ io.ReaderAt = (*openFile)(nil)
343 )
344 345 func (f *openFile) Close() error { return nil }
346 func (f *openFile) Stat() (fs.FileInfo, error) { return f.f, nil }
347 348 func (f *openFile) Read(b []byte) (int, error) {
349 if f.offset >= int64(len(f.f.data)) {
350 return 0, io.EOF
351 }
352 if f.offset < 0 {
353 return 0, &fs.PathError{Op: "read", Path: f.f.name, Err: fs.ErrInvalid}
354 }
355 n := copy(b, f.f.data[f.offset:])
356 f.offset += int64(n)
357 return n, nil
358 }
359 360 func (f *openFile) Seek(offset int64, whence int) (int64, error) {
361 switch whence {
362 case 0:
363 // offset += 0
364 case 1:
365 offset += f.offset
366 case 2:
367 offset += int64(len(f.f.data))
368 }
369 if offset < 0 || offset > int64(len(f.f.data)) {
370 return 0, &fs.PathError{Op: "seek", Path: f.f.name, Err: fs.ErrInvalid}
371 }
372 f.offset = offset
373 return offset, nil
374 }
375 376 func (f *openFile) ReadAt(b []byte, offset int64) (int, error) {
377 if offset < 0 || offset > int64(len(f.f.data)) {
378 return 0, &fs.PathError{Op: "read", Path: f.f.name, Err: fs.ErrInvalid}
379 }
380 n := copy(b, f.f.data[offset:])
381 if n < len(b) {
382 return n, io.EOF
383 }
384 return n, nil
385 }
386 387 // An openDir is a directory open for reading.
388 type openDir struct {
389 f *file // the directory file itself
390 files []file // the directory contents
391 offset int // the read offset, an index into the files slice
392 }
393 394 func (d *openDir) Close() error { return nil }
395 func (d *openDir) Stat() (fs.FileInfo, error) { return d.f, nil }
396 397 func (d *openDir) Read([]byte) (int, error) {
398 return 0, &fs.PathError{Op: "read", Path: d.f.name, Err: errors.New("is a directory")}
399 }
400 401 func (d *openDir) ReadDir(count int) ([]fs.DirEntry, error) {
402 n := len(d.files) - d.offset
403 if n == 0 {
404 if count <= 0 {
405 return nil, nil
406 }
407 return nil, io.EOF
408 }
409 if count > 0 && n > count {
410 n = count
411 }
412 list := []fs.DirEntry{:n}
413 for i := range list {
414 list[i] = &d.files[d.offset+i]
415 }
416 d.offset += n
417 return list, nil
418 }
419 420 // sortSearch is like sort.Search, avoiding an import.
421 func sortSearch(n int, f func(int) bool) int {
422 // Define f(-1) == false and f(n) == true.
423 // Invariant: f(i-1) == false, f(j) == true.
424 i, j := 0, n
425 for i < j {
426 h := int(uint(i+j) >> 1) // avoid overflow when computing h
427 // i ≤ h < j
428 if !f(h) {
429 i = h + 1 // preserves f(i-1) == false
430 } else {
431 j = h // preserves f(j) == true
432 }
433 }
434 // i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i.
435 return i
436 }
437