]> ruderich.org/simon Gitweb - safcm/safcm.git/blob - cmd/safcm/fixperms.go
Use SPDX license identifiers
[safcm/safcm.git] / cmd / safcm / fixperms.go
1 // "fixperms" sub-command: apply proper permissions in files/ directories
2
3 // SPDX-License-Identifier: GPL-3.0-or-later
4 // Copyright (C) 2021-2024  Simon Ruderich
5
6 package main
7
8 import (
9         "fmt"
10         "io/fs"
11         "log"
12         "os"
13         "path/filepath"
14
15         "ruderich.org/simon/safcm/cmd/safcm/config"
16         "ruderich.org/simon/safcm/remote/sync"
17 )
18
19 func MainFixperms() error {
20         _, _, _, err := LoadBaseFiles()
21         if err != nil {
22                 return fmt.Errorf("not in a safcm directory: %v", err)
23         }
24
25         xs, err := os.ReadDir(".")
26         if err != nil {
27                 return err
28         }
29         for _, x := range xs {
30                 if !x.IsDir() {
31                         continue
32                 }
33                 path := filepath.Join(x.Name(), "files")
34
35                 err := filepath.WalkDir(path, fixpermsWalkDirFunc)
36                 if err != nil {
37                         if os.IsNotExist(err) {
38                                 continue
39                         }
40                         return fmt.Errorf("%s: %v", path, err)
41                 }
42         }
43
44         return nil
45 }
46
47 func fixpermsWalkDirFunc(path string, d fs.DirEntry, err error) error {
48         if err != nil {
49                 return err
50         }
51
52         info, err := d.Info()
53         if err != nil {
54                 return err
55         }
56         typ := info.Mode().Type()
57         perm := config.FileModeToFullPerm(info.Mode())
58
59         if typ == 0 /* regular file */ {
60                 if perm != 0644 && perm != 0755 {
61                         if perm&0111 != 0 /* executable */ {
62                                 perm = 0755
63                         } else {
64                                 perm = 0644
65                         }
66                         log.Printf("chmodding %q to %#o", path, perm)
67                         // This is safe because perm does not include
68                         // setuid/setgid/sticky which use different values in
69                         // FileMode.
70                         err := chmodNoFollow(path, fs.FileMode(perm))
71                         if err != nil {
72                                 return err
73                         }
74                 }
75         } else if typ == fs.ModeDir {
76                 if perm != 0755 {
77                         perm = 0755
78                         log.Printf("chmodding %q to %#o", path, perm)
79                         err := chmodNoFollow(path, fs.FileMode(perm))
80                         if err != nil {
81                                 return err
82                         }
83                 }
84         }
85         // Other file types are caught by regular "sync"
86
87         return nil
88 }
89
90 // chmodNoFollow works like os.Chmod but doesn't follow symlinks.
91 func chmodNoFollow(path string, mode fs.FileMode) error {
92         x, err := sync.OpenFileNoSymlinks(path)
93         if err != nil {
94                 return err
95         }
96         defer x.Close()
97
98         err = x.Chmod(mode)
99         if err != nil {
100                 return err
101         }
102
103         return nil
104 }