| package filemode |
| |
| import ( |
| "encoding/binary" |
| "fmt" |
| "os" |
| "strconv" |
| ) |
| |
| // A FileMode represents the kind of tree entries used by git. It |
| // resembles regular file systems modes, although FileModes are |
| // considerably simpler (there are not so many), and there are some, |
| // like Submodule that has no file system equivalent. |
| type FileMode uint32 |
| |
| const ( |
| // Empty is used as the FileMode of tree elements when comparing |
| // trees in the following situations: |
| // |
| // - the mode of tree elements before their creation. - the mode of |
| // tree elements after their deletion. - the mode of unmerged |
| // elements when checking the index. |
| // |
| // Empty has no file system equivalent. As Empty is the zero value |
| // of FileMode, it is also returned by New and |
| // NewFromOsNewFromOSFileMode along with an error, when they fail. |
| Empty FileMode = 0 |
| // Dir represent a Directory. |
| Dir FileMode = 0040000 |
| // Regular represent non-executable files. Please note this is not |
| // the same as golang regular files, which include executable files. |
| Regular FileMode = 0100644 |
| // Deprecated represent non-executable files with the group writable |
| // bit set. This mode was supported by the first versions of git, |
| // but it has been deprecatred nowadays. This library uses them |
| // internally, so you can read old packfiles, but will treat them as |
| // Regulars when interfacing with the outside world. This is the |
| // standard git behaviuor. |
| Deprecated FileMode = 0100664 |
| // Executable represents executable files. |
| Executable FileMode = 0100755 |
| // Symlink represents symbolic links to files. |
| Symlink FileMode = 0120000 |
| // Submodule represents git submodules. This mode has no file system |
| // equivalent. |
| Submodule FileMode = 0160000 |
| ) |
| |
| // New takes the octal string representation of a FileMode and returns |
| // the FileMode and a nil error. If the string can not be parsed to a |
| // 32 bit unsigned octal number, it returns Empty and the parsing error. |
| // |
| // Example: "40000" means Dir, "100644" means Regular. |
| // |
| // Please note this function does not check if the returned FileMode |
| // is valid in git or if it is malformed. For instance, "1" will |
| // return the malformed FileMode(1) and a nil error. |
| func New(s string) (FileMode, error) { |
| n, err := strconv.ParseUint(s, 8, 32) |
| if err != nil { |
| return Empty, err |
| } |
| |
| return FileMode(n), nil |
| } |
| |
| // NewFromOSFileMode returns the FileMode used by git to represent |
| // the provided file system modes and a nil error on success. If the |
| // file system mode cannot be mapped to any valid git mode (as with |
| // sockets or named pipes), it will return Empty and an error. |
| // |
| // Note that some git modes cannot be generated from os.FileModes, like |
| // Deprecated and Submodule; while Empty will be returned, along with an |
| // error, only when the method fails. |
| func NewFromOSFileMode(m os.FileMode) (FileMode, error) { |
| if m.IsRegular() { |
| if isSetTemporary(m) { |
| return Empty, fmt.Errorf("no equivalent git mode for %s", m) |
| } |
| if isSetCharDevice(m) { |
| return Empty, fmt.Errorf("no equivalent git mode for %s", m) |
| } |
| if isSetUserExecutable(m) { |
| return Executable, nil |
| } |
| return Regular, nil |
| } |
| |
| if m.IsDir() { |
| return Dir, nil |
| } |
| |
| if isSetSymLink(m) { |
| return Symlink, nil |
| } |
| |
| return Empty, fmt.Errorf("no equivalent git mode for %s", m) |
| } |
| |
| func isSetCharDevice(m os.FileMode) bool { |
| return m&os.ModeCharDevice != 0 |
| } |
| |
| func isSetTemporary(m os.FileMode) bool { |
| return m&os.ModeTemporary != 0 |
| } |
| |
| func isSetUserExecutable(m os.FileMode) bool { |
| return m&0100 != 0 |
| } |
| |
| func isSetSymLink(m os.FileMode) bool { |
| return m&os.ModeSymlink != 0 |
| } |
| |
| // Bytes return a slice of 4 bytes with the mode in little endian |
| // encoding. |
| func (m FileMode) Bytes() []byte { |
| ret := make([]byte, 4) |
| binary.LittleEndian.PutUint32(ret, uint32(m)) |
| return ret[:] |
| } |
| |
| // IsMalformed returns if the FileMode should not appear in a git packfile, |
| // this is: Empty and any other mode not mentioned as a constant in this |
| // package. |
| func (m FileMode) IsMalformed() bool { |
| return m != Dir && |
| m != Regular && |
| m != Deprecated && |
| m != Executable && |
| m != Symlink && |
| m != Submodule |
| } |
| |
| // String returns the FileMode as a string in the standatd git format, |
| // this is, an octal number padded with ceros to 7 digits. Malformed |
| // modes are printed in that same format, for easier debugging. |
| // |
| // Example: Regular is "0100644", Empty is "0000000". |
| func (m FileMode) String() string { |
| return fmt.Sprintf("%07o", uint32(m)) |
| } |
| |
| // IsRegular returns if the FileMode represents that of a regular file, |
| // this is, either Regular or Deprecated. Please note that Executable |
| // are not regular even though in the UNIX tradition, they usually are: |
| // See the IsFile method. |
| func (m FileMode) IsRegular() bool { |
| return m == Regular || |
| m == Deprecated |
| } |
| |
| // IsFile returns if the FileMode represents that of a file, this is, |
| // Regular, Deprecated, Excutable or Link. |
| func (m FileMode) IsFile() bool { |
| return m == Regular || |
| m == Deprecated || |
| m == Executable || |
| m == Symlink |
| } |
| |
| // ToOSFileMode returns the os.FileMode to be used when creating file |
| // system elements with the given git mode and a nil error on success. |
| // |
| // When the provided mode cannot be mapped to a valid file system mode |
| // (e.g. Submodule) it returns os.FileMode(0) and an error. |
| // |
| // The returned file mode does not take into account the umask. |
| func (m FileMode) ToOSFileMode() (os.FileMode, error) { |
| switch m { |
| case Dir: |
| return os.ModePerm | os.ModeDir, nil |
| case Submodule: |
| return os.ModePerm | os.ModeDir, nil |
| case Regular: |
| return os.FileMode(0644), nil |
| // Deprecated is no longer allowed: treated as a Regular instead |
| case Deprecated: |
| return os.FileMode(0644), nil |
| case Executable: |
| return os.FileMode(0755), nil |
| case Symlink: |
| return os.ModePerm | os.ModeSymlink, nil |
| } |
| |
| return os.FileMode(0), fmt.Errorf("malformed mode (%s)", m) |
| } |