fix: let umask do the thing (#10723)
We had a few places where we had perhaps too much of an opinion on the permissions on created files and directories, sometimes fuled by a misconception about how permissions work in both Unix and Windows. Recap on the ground rules: - On all unixes, all file & directory creation (`Mkdir`, `MkdirAll`, `Create`, `WriteFile`, `Open`) has the given permission bits filtered via the user's umask. The proper permissions for us to use are in almost all cases 0o666 for files and 0o777 for directories, strange as that may look at the call site. - On Windows, there is no umask but in turn all of the permission bits except the user write bit are ignored. The absence of user write bit is converted into the read only attribute. This means that what is proper for Unix above is also proper for Windows. - We make an exception when creating files for certificate keys and the config / database directories, as those contain secrets we think should remain closed even if the user generally collaborates with other users on the system. (Also removal of a bugfixed copy of MkdirAll for Windows that hasn't been necessary for a few years.) --------- Signed-off-by: Jakob Borg <jakob@kastelo.net>
This commit is contained in:
@@ -718,7 +718,7 @@ func shouldBuildSyso(dir string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
jsonPath := filepath.Join(dir, "versioninfo.json")
|
jsonPath := filepath.Join(dir, "versioninfo.json")
|
||||||
err = os.WriteFile(jsonPath, bs, 0o644)
|
err = os.WriteFile(jsonPath, bs, 0o666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.New("failed to create " + jsonPath + ": " + err.Error())
|
return "", errors.New("failed to create " + jsonPath + ": " + err.Error())
|
||||||
}
|
}
|
||||||
@@ -783,7 +783,7 @@ func copyFile(src, dst string, perm os.FileMode) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
copy:
|
copy:
|
||||||
os.MkdirAll(filepath.Dir(dst), 0o777)
|
os.MkdirAll(filepath.Dir(dst), os.ModePerm)
|
||||||
if err := os.WriteFile(dst, in, perm); err != nil {
|
if err := os.WriteFile(dst, in, perm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1432,7 +1432,7 @@ func writeCompatJSON() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
bs, _ := json.MarshalIndent(e, "", " ")
|
bs, _ := json.MarshalIndent(e, "", " ")
|
||||||
if err := os.WriteFile("compat.json", bs, 0o644); err != nil {
|
if err := os.WriteFile("compat.json", bs, 0o666); err != nil {
|
||||||
log.Fatal("Writing compat.json:", err)
|
log.Fatal("Writing compat.json:", err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ type currentFile struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *diskStore) Serve(ctx context.Context) {
|
func (d *diskStore) Serve(ctx context.Context) {
|
||||||
if err := os.MkdirAll(d.dir, 0o700); err != nil {
|
if err := os.MkdirAll(d.dir, os.ModePerm); err != nil {
|
||||||
log.Println("Creating directory:", err)
|
log.Println("Creating directory:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,7 @@ func (d *diskStore) Serve(ctx context.Context) {
|
|||||||
case entry := <-d.inbox:
|
case entry := <-d.inbox:
|
||||||
path := d.fullPath(entry.path)
|
path := d.fullPath(entry.path)
|
||||||
|
|
||||||
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
|
if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
|
||||||
log.Println("Creating directory:", err)
|
log.Println("Creating directory:", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -77,7 +77,7 @@ func (d *diskStore) Serve(ctx context.Context) {
|
|||||||
log.Println("Failed to compress crash report:", err)
|
log.Println("Failed to compress crash report:", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := os.WriteFile(path, buf.Bytes(), 0o600); err != nil {
|
if err := os.WriteFile(path, buf.Bytes(), 0o666); err != nil {
|
||||||
log.Printf("Failed to write %s: %v", entry.path, err)
|
log.Printf("Failed to write %s: %v", entry.path, err)
|
||||||
_ = os.Remove(path)
|
_ = os.Remove(path)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -52,5 +52,5 @@ func compressAndWrite(bs []byte, fullPath string) error {
|
|||||||
gw.Close()
|
gw.Close()
|
||||||
|
|
||||||
// Create an output file with the compressed report
|
// Create an output file with the compressed report
|
||||||
return os.WriteFile(fullPath, buf.Bytes(), 0o644)
|
return os.WriteFile(fullPath, buf.Bytes(), 0o666)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -612,7 +612,7 @@ func saveRelays(file string, relays []*relay) error {
|
|||||||
for _, relay := range relays {
|
for _, relay := range relays {
|
||||||
content += relay.uri.String() + "\n"
|
content += relay.uri.String() + "\n"
|
||||||
}
|
}
|
||||||
return os.WriteFile(file, []byte(content), 0o777)
|
return os.WriteFile(file, []byte(content), 0o666)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTestCertificate() tls.Certificate {
|
func createTestCertificate() tls.Certificate {
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ func (c *CLI) process(srcFs fs.Filesystem, dstFs fs.Filesystem, path string) err
|
|||||||
|
|
||||||
var plainFd fs.File
|
var plainFd fs.File
|
||||||
if dstFs != nil {
|
if dstFs != nil {
|
||||||
if err := dstFs.MkdirAll(filepath.Dir(plainFi.Name), 0o700); err != nil {
|
if err := dstFs.MkdirAll(filepath.Dir(plainFi.Name), fs.ModePerm); err != nil {
|
||||||
return fmt.Errorf("%s: %w", plainFi.Name, err)
|
return fmt.Errorf("%s: %w", plainFi.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -673,7 +673,7 @@ func auditWriter(auditFile string) io.Writer {
|
|||||||
} else {
|
} else {
|
||||||
auditFlags = os.O_WRONLY | os.O_CREATE | os.O_APPEND
|
auditFlags = os.O_WRONLY | os.O_CREATE | os.O_APPEND
|
||||||
}
|
}
|
||||||
fd, err = os.OpenFile(auditFile, auditFlags, 0o600)
|
fd, err = os.OpenFile(auditFile, auditFlags, 0o666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Failed to open audit file", slogutil.Error(err))
|
slog.Error("Failed to open audit file", slogutil.Error(err))
|
||||||
os.Exit(svcutil.ExitError.AsInt())
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
|
|||||||
@@ -479,7 +479,7 @@ func (f *autoclosedFile) ensureOpenLocked() error {
|
|||||||
// We open the file for write only, and create it if it doesn't exist.
|
// We open the file for write only, and create it if it doesn't exist.
|
||||||
flags := os.O_WRONLY | os.O_CREATE | os.O_APPEND
|
flags := os.O_WRONLY | os.O_CREATE | os.O_APPEND
|
||||||
|
|
||||||
fd, err := os.OpenFile(f.name, flags, 0o644)
|
fd, err := os.OpenFile(f.name, flags, 0o666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1248,7 +1248,7 @@ func (s *service) getSupportBundle(w http.ResponseWriter, r *http.Request) {
|
|||||||
zipFilePath := filepath.Join(locations.GetBaseDir(locations.ConfigBaseDir), zipFileName)
|
zipFilePath := filepath.Join(locations.GetBaseDir(locations.ConfigBaseDir), zipFileName)
|
||||||
|
|
||||||
// Write buffer zip to local zip file (back up)
|
// Write buffer zip to local zip file (back up)
|
||||||
if err := os.WriteFile(zipFilePath, zipFilesBuffer.Bytes(), 0o600); err != nil {
|
if err := os.WriteFile(zipFilePath, zipFilesBuffer.Bytes(), 0o666); err != nil {
|
||||||
slog.Warn("Failed to create support bundle zip (file)", slogutil.FilePath(zipFilePath), slogutil.Error(err))
|
slog.Warn("Failed to create support bundle zip (file)", slogutil.FilePath(zipFilePath), slogutil.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ func (f *FolderConfiguration) CreateMarker() error {
|
|||||||
ffs := f.Filesystem()
|
ffs := f.Filesystem()
|
||||||
|
|
||||||
// Create the marker as a directory
|
// Create the marker as a directory
|
||||||
err := ffs.Mkdir(DefaultMarkerName, 0o755)
|
err := ffs.Mkdir(DefaultMarkerName, fs.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -176,7 +176,7 @@ func (f *FolderConfiguration) CreateMarker() error {
|
|||||||
// Create a file inside it, reducing the risk of the marker directory
|
// Create a file inside it, reducing the risk of the marker directory
|
||||||
// being removed by automated cleanup tools.
|
// being removed by automated cleanup tools.
|
||||||
markerFile := filepath.Join(DefaultMarkerName, f.markerFilename())
|
markerFile := filepath.Join(DefaultMarkerName, f.markerFilename())
|
||||||
if err := fs.WriteFile(ffs, markerFile, f.markerContents(), 0o644); err != nil {
|
if err := fs.WriteFile(ffs, markerFile, f.markerContents(), 0o666); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,19 +246,10 @@ func (f *FolderConfiguration) checkFilesystemPath(ffs fs.Filesystem, path string
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *FolderConfiguration) CreateRoot() (err error) {
|
func (f *FolderConfiguration) CreateRoot() (err error) {
|
||||||
// Directory permission bits. Will be filtered down to something
|
|
||||||
// sane by umask on Unixes.
|
|
||||||
permBits := fs.FileMode(0o777)
|
|
||||||
if build.IsWindows {
|
|
||||||
// Windows has no umask so we must chose a safer set of bits to
|
|
||||||
// begin with.
|
|
||||||
permBits = 0o700
|
|
||||||
}
|
|
||||||
|
|
||||||
filesystem := f.Filesystem()
|
filesystem := f.Filesystem()
|
||||||
|
|
||||||
if _, err = filesystem.Stat("."); fs.IsNotExist(err) {
|
if _, err = filesystem.Stat("."); fs.IsNotExist(err) {
|
||||||
err = filesystem.MkdirAll(".", permBits)
|
err = filesystem.MkdirAll(".", fs.ModePerm)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/syncthing/syncthing/internal/slogutil"
|
"github.com/syncthing/syncthing/internal/slogutil"
|
||||||
"github.com/syncthing/syncthing/lib/build"
|
|
||||||
"github.com/syncthing/syncthing/lib/fs"
|
"github.com/syncthing/syncthing/lib/fs"
|
||||||
"github.com/syncthing/syncthing/lib/netutil"
|
"github.com/syncthing/syncthing/lib/netutil"
|
||||||
"github.com/syncthing/syncthing/lib/upgrade"
|
"github.com/syncthing/syncthing/lib/upgrade"
|
||||||
@@ -224,27 +223,20 @@ func migrateToConfigV24(cfg *Configuration) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func migrateToConfigV23(cfg *Configuration) {
|
func migrateToConfigV23(cfg *Configuration) {
|
||||||
permBits := fs.FileMode(0o777)
|
|
||||||
if build.IsWindows {
|
|
||||||
// Windows has no umask so we must chose a safer set of bits to
|
|
||||||
// begin with.
|
|
||||||
permBits = 0o700
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upgrade code remains hardcoded for .stfolder despite configurable
|
// Upgrade code remains hardcoded for .stfolder despite configurable
|
||||||
// marker name in later versions.
|
// marker name in later versions.
|
||||||
|
|
||||||
for i := range cfg.Folders {
|
for i := range cfg.Folders {
|
||||||
fs := cfg.Folders[i].Filesystem()
|
ffs := cfg.Folders[i].Filesystem()
|
||||||
// Invalid config posted, or tests.
|
// Invalid config posted, or tests.
|
||||||
if fs == nil {
|
if ffs == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if stat, err := fs.Stat(DefaultMarkerName); err == nil && !stat.IsDir() {
|
if stat, err := ffs.Stat(DefaultMarkerName); err == nil && !stat.IsDir() {
|
||||||
err = fs.Remove(DefaultMarkerName)
|
err = ffs.Remove(DefaultMarkerName)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = fs.Mkdir(DefaultMarkerName, permBits)
|
err = ffs.Mkdir(DefaultMarkerName, fs.ModePerm)
|
||||||
fs.Hide(DefaultMarkerName) // ignore error
|
ffs.Hide(DefaultMarkerName) // ignore error
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Warn("Failed to upgrade folder marker", slogutil.Error(err))
|
slog.Warn("Failed to upgrade folder marker", slogutil.Error(err))
|
||||||
|
|||||||
+1
-1
@@ -174,7 +174,7 @@ func (f *BasicFilesystem) MkdirAll(path string, perm FileMode) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return f.mkdirAll(path, os.FileMode(perm))
|
return os.MkdirAll(path, os.FileMode(perm))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *BasicFilesystem) Lstat(name string) (FileInfo, error) {
|
func (f *BasicFilesystem) Lstat(name string) (FileInfo, error) {
|
||||||
|
|||||||
@@ -32,10 +32,6 @@ func (f *BasicFilesystem) ReadSymlink(name string) (string, error) {
|
|||||||
return os.Readlink(name)
|
return os.Readlink(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*BasicFilesystem) mkdirAll(path string, perm os.FileMode) error {
|
|
||||||
return os.MkdirAll(path, perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unhide is a noop on unix, as unhiding files requires renaming them.
|
// Unhide is a noop on unix, as unhiding files requires renaming them.
|
||||||
// We still check that the relative path does not try to escape the root
|
// We still check that the relative path does not try to escape the root
|
||||||
func (f *BasicFilesystem) Unhide(name string) error {
|
func (f *BasicFilesystem) Unhide(name string) error {
|
||||||
|
|||||||
@@ -31,57 +31,6 @@ func (BasicFilesystem) CreateSymlink(target, name string) error {
|
|||||||
return errNotSupported
|
return errNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
// Required due to https://github.com/golang/go/issues/10900
|
|
||||||
func (f *BasicFilesystem) mkdirAll(path string, perm os.FileMode) error {
|
|
||||||
// Fast path: if we can tell whether path is a directory or file, stop with success or error.
|
|
||||||
dir, err := os.Stat(path)
|
|
||||||
if err == nil {
|
|
||||||
if dir.IsDir() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &os.PathError{
|
|
||||||
Op: "mkdir",
|
|
||||||
Path: path,
|
|
||||||
Err: syscall.ENOTDIR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slow path: make sure parent exists and then call Mkdir for path.
|
|
||||||
i := len(path)
|
|
||||||
for i > 0 && IsPathSeparator(path[i-1]) { // Skip trailing path separator.
|
|
||||||
i--
|
|
||||||
}
|
|
||||||
|
|
||||||
j := i
|
|
||||||
for j > 0 && !IsPathSeparator(path[j-1]) { // Scan backward over element.
|
|
||||||
j--
|
|
||||||
}
|
|
||||||
|
|
||||||
if j > 1 {
|
|
||||||
// Create parent
|
|
||||||
parent := path[0 : j-1]
|
|
||||||
if parent != filepath.VolumeName(parent) {
|
|
||||||
err = f.mkdirAll(parent, perm)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parent now exists; invoke Mkdir and use its result.
|
|
||||||
err = os.Mkdir(path, perm)
|
|
||||||
if err != nil {
|
|
||||||
// Handle arguments like "foo/." by
|
|
||||||
// double-checking that directory doesn't exist.
|
|
||||||
dir, err1 := os.Lstat(path)
|
|
||||||
if err1 == nil && dir.IsDir() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *BasicFilesystem) Unhide(name string) error {
|
func (f *BasicFilesystem) Unhide(name string) error {
|
||||||
name, err := f.rooted(name)
|
name, err := f.rooted(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+2
-2
@@ -153,7 +153,7 @@ func newFakeFilesystem(rootURI string, _ ...Option) *fakeFS {
|
|||||||
for (files == 0 || createdFiles < files) && (maxsize == 0 || writtenData>>20 < int64(maxsize)) {
|
for (files == 0 || createdFiles < files) && (maxsize == 0 || writtenData>>20 < int64(maxsize)) {
|
||||||
dir := filepath.Join(fmt.Sprintf("%02x", rng.Intn(255)), fmt.Sprintf("%02x", rng.Intn(255)))
|
dir := filepath.Join(fmt.Sprintf("%02x", rng.Intn(255)), fmt.Sprintf("%02x", rng.Intn(255)))
|
||||||
file := fmt.Sprintf("%016x", rng.Int63())
|
file := fmt.Sprintf("%016x", rng.Int63())
|
||||||
_ = fs.MkdirAll(dir, 0o755)
|
_ = fs.MkdirAll(dir, ModePerm)
|
||||||
|
|
||||||
fd, _ := fs.Create(filepath.Join(dir, file))
|
fd, _ := fs.Create(filepath.Join(dir, file))
|
||||||
createdFiles++
|
createdFiles++
|
||||||
@@ -169,7 +169,7 @@ func newFakeFilesystem(rootURI string, _ ...Option) *fakeFS {
|
|||||||
|
|
||||||
if !nostfolder {
|
if !nostfolder {
|
||||||
// Also create a default folder marker for good measure
|
// Also create a default folder marker for good measure
|
||||||
_ = fs.Mkdir(".stfolder", 0o700)
|
_ = fs.Mkdir(".stfolder", ModePerm)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only set the latency after doing the operations required to create
|
// We only set the latency after doing the operations required to create
|
||||||
|
|||||||
@@ -701,7 +701,7 @@ func (f *sendReceiveFolder) checkParent(file string, scanChan chan<- string) boo
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
f.sl.Debug("Creating parent directory", slogutil.FilePath(file))
|
f.sl.Debug("Creating parent directory", slogutil.FilePath(file))
|
||||||
if err := f.mtimefs.MkdirAll(parent, 0o755); err != nil {
|
if err := f.mtimefs.MkdirAll(parent, fs.ModePerm); err != nil {
|
||||||
f.newPullError(file, fmt.Errorf("creating parent dir: %w", err))
|
f.newPullError(file, fmt.Errorf("creating parent dir: %w", err))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1784,7 +1784,7 @@ func (m *model) handleAutoAccepts(deviceID protocol.DeviceID, folder protocol.Fo
|
|||||||
|
|
||||||
// Attempt to create it to make sure it does, now.
|
// Attempt to create it to make sure it does, now.
|
||||||
fullPath := filepath.Join(defaultFolderCfg.Path, path)
|
fullPath := filepath.Join(defaultFolderCfg.Path, path)
|
||||||
if err := defaultPathFs.MkdirAll(path, 0o700); err != nil {
|
if err := defaultPathFs.MkdirAll(path, fs.ModePerm); err != nil {
|
||||||
slog.Error("Failed to create path for auto-accepted folder", folder.LogAttr(), slogutil.FilePath(fullPath), slogutil.Error(err))
|
slog.Error("Failed to create path for auto-accepted folder", folder.LogAttr(), slogutil.FilePath(fullPath), slogutil.Error(err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ func archiveFile(method fs.CopyRangeMethod, srcFs, dstFs fs.Filesystem, filePath
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if fs.IsNotExist(err) {
|
if fs.IsNotExist(err) {
|
||||||
slog.Debug("Creating versions dir")
|
slog.Debug("Creating versions dir")
|
||||||
err := dstFs.MkdirAll(".", 0o755)
|
err := dstFs.MkdirAll(".", fs.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -328,7 +328,7 @@ func restoreFile(method fs.CopyRangeMethod, src, dst fs.Filesystem, filePath str
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = dst.MkdirAll(filepath.Dir(filePath), 0o755)
|
_ = dst.MkdirAll(filepath.Dir(filePath), fs.ModePerm)
|
||||||
err := osutil.RenameOrCopy(method, src, dst, sourceFile, filePath)
|
err := osutil.RenameOrCopy(method, src, dst, sourceFile, filePath)
|
||||||
_ = dst.Chtimes(filePath, sourceMtime, sourceMtime)
|
_ = dst.Chtimes(filePath, sourceMtime, sourceMtime)
|
||||||
return err
|
return err
|
||||||
|
|||||||
+1
-1
@@ -93,7 +93,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
bs = authorsRe.ReplaceAll(bs, []byte("id=\"contributor-list\">\n"+replacement+"\n </div>"))
|
bs = authorsRe.ReplaceAll(bs, []byte("id=\"contributor-list\">\n"+replacement+"\n </div>"))
|
||||||
|
|
||||||
if err := os.WriteFile(htmlFile, bs, 0o644); err != nil {
|
if err := os.WriteFile(htmlFile, bs, 0o666); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -265,7 +265,7 @@ func readAll(path string) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func writeFile(path string, data string) {
|
func writeFile(path string, data string) {
|
||||||
err := os.WriteFile(path, []byte(data), 0o644)
|
err := os.WriteFile(path, []byte(data), 0o666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user