You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

filemode.go 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. package filemode
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "os"
  6. "strconv"
  7. )
  8. // A FileMode represents the kind of tree entries used by git. It
  9. // resembles regular file systems modes, although FileModes are
  10. // considerably simpler (there are not so many), and there are some,
  11. // like Submodule that has no file system equivalent.
  12. type FileMode uint32
  13. const (
  14. // Empty is used as the FileMode of tree elements when comparing
  15. // trees in the following situations:
  16. //
  17. // - the mode of tree elements before their creation. - the mode of
  18. // tree elements after their deletion. - the mode of unmerged
  19. // elements when checking the index.
  20. //
  21. // Empty has no file system equivalent. As Empty is the zero value
  22. // of FileMode, it is also returned by New and
  23. // NewFromOsNewFromOSFileMode along with an error, when they fail.
  24. Empty FileMode = 0
  25. // Dir represent a Directory.
  26. Dir FileMode = 0040000
  27. // Regular represent non-executable files. Please note this is not
  28. // the same as golang regular files, which include executable files.
  29. Regular FileMode = 0100644
  30. // Deprecated represent non-executable files with the group writable
  31. // bit set. This mode was supported by the first versions of git,
  32. // but it has been deprecated nowadays. This library uses them
  33. // internally, so you can read old packfiles, but will treat them as
  34. // Regulars when interfacing with the outside world. This is the
  35. // standard git behaviour.
  36. Deprecated FileMode = 0100664
  37. // Executable represents executable files.
  38. Executable FileMode = 0100755
  39. // Symlink represents symbolic links to files.
  40. Symlink FileMode = 0120000
  41. // Submodule represents git submodules. This mode has no file system
  42. // equivalent.
  43. Submodule FileMode = 0160000
  44. )
  45. // New takes the octal string representation of a FileMode and returns
  46. // the FileMode and a nil error. If the string can not be parsed to a
  47. // 32 bit unsigned octal number, it returns Empty and the parsing error.
  48. //
  49. // Example: "40000" means Dir, "100644" means Regular.
  50. //
  51. // Please note this function does not check if the returned FileMode
  52. // is valid in git or if it is malformed. For instance, "1" will
  53. // return the malformed FileMode(1) and a nil error.
  54. func New(s string) (FileMode, error) {
  55. n, err := strconv.ParseUint(s, 8, 32)
  56. if err != nil {
  57. return Empty, err
  58. }
  59. return FileMode(n), nil
  60. }
  61. // NewFromOSFileMode returns the FileMode used by git to represent
  62. // the provided file system modes and a nil error on success. If the
  63. // file system mode cannot be mapped to any valid git mode (as with
  64. // sockets or named pipes), it will return Empty and an error.
  65. //
  66. // Note that some git modes cannot be generated from os.FileModes, like
  67. // Deprecated and Submodule; while Empty will be returned, along with an
  68. // error, only when the method fails.
  69. func NewFromOSFileMode(m os.FileMode) (FileMode, error) {
  70. if m.IsRegular() {
  71. if isSetTemporary(m) {
  72. return Empty, fmt.Errorf("no equivalent git mode for %s", m)
  73. }
  74. if isSetCharDevice(m) {
  75. return Empty, fmt.Errorf("no equivalent git mode for %s", m)
  76. }
  77. if isSetUserExecutable(m) {
  78. return Executable, nil
  79. }
  80. return Regular, nil
  81. }
  82. if m.IsDir() {
  83. return Dir, nil
  84. }
  85. if isSetSymLink(m) {
  86. return Symlink, nil
  87. }
  88. return Empty, fmt.Errorf("no equivalent git mode for %s", m)
  89. }
  90. func isSetCharDevice(m os.FileMode) bool {
  91. return m&os.ModeCharDevice != 0
  92. }
  93. func isSetTemporary(m os.FileMode) bool {
  94. return m&os.ModeTemporary != 0
  95. }
  96. func isSetUserExecutable(m os.FileMode) bool {
  97. return m&0100 != 0
  98. }
  99. func isSetSymLink(m os.FileMode) bool {
  100. return m&os.ModeSymlink != 0
  101. }
  102. // Bytes return a slice of 4 bytes with the mode in little endian
  103. // encoding.
  104. func (m FileMode) Bytes() []byte {
  105. ret := make([]byte, 4)
  106. binary.LittleEndian.PutUint32(ret, uint32(m))
  107. return ret[:]
  108. }
  109. // IsMalformed returns if the FileMode should not appear in a git packfile,
  110. // this is: Empty and any other mode not mentioned as a constant in this
  111. // package.
  112. func (m FileMode) IsMalformed() bool {
  113. return m != Dir &&
  114. m != Regular &&
  115. m != Deprecated &&
  116. m != Executable &&
  117. m != Symlink &&
  118. m != Submodule
  119. }
  120. // String returns the FileMode as a string in the standatd git format,
  121. // this is, an octal number padded with ceros to 7 digits. Malformed
  122. // modes are printed in that same format, for easier debugging.
  123. //
  124. // Example: Regular is "0100644", Empty is "0000000".
  125. func (m FileMode) String() string {
  126. return fmt.Sprintf("%07o", uint32(m))
  127. }
  128. // IsRegular returns if the FileMode represents that of a regular file,
  129. // this is, either Regular or Deprecated. Please note that Executable
  130. // are not regular even though in the UNIX tradition, they usually are:
  131. // See the IsFile method.
  132. func (m FileMode) IsRegular() bool {
  133. return m == Regular ||
  134. m == Deprecated
  135. }
  136. // IsFile returns if the FileMode represents that of a file, this is,
  137. // Regular, Deprecated, Executable or Link.
  138. func (m FileMode) IsFile() bool {
  139. return m == Regular ||
  140. m == Deprecated ||
  141. m == Executable ||
  142. m == Symlink
  143. }
  144. // ToOSFileMode returns the os.FileMode to be used when creating file
  145. // system elements with the given git mode and a nil error on success.
  146. //
  147. // When the provided mode cannot be mapped to a valid file system mode
  148. // (e.g. Submodule) it returns os.FileMode(0) and an error.
  149. //
  150. // The returned file mode does not take into account the umask.
  151. func (m FileMode) ToOSFileMode() (os.FileMode, error) {
  152. switch m {
  153. case Dir:
  154. return os.ModePerm | os.ModeDir, nil
  155. case Submodule:
  156. return os.ModePerm | os.ModeDir, nil
  157. case Regular:
  158. return os.FileMode(0644), nil
  159. // Deprecated is no longer allowed: treated as a Regular instead
  160. case Deprecated:
  161. return os.FileMode(0644), nil
  162. case Executable:
  163. return os.FileMode(0755), nil
  164. case Symlink:
  165. return os.ModePerm | os.ModeSymlink, nil
  166. }
  167. return os.FileMode(0), fmt.Errorf("malformed mode (%s)", m)
  168. }