;;; cpio-modes.el --- handle file modes/permissions. -*- coding: utf-8 -*-
;; COPYRIGHT
;;
;; Copyright © 2019-2020 Free Software Foundation, Inc.
;; All rights reserved.
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see .
;; Author: Douglas Lewan
;; Maintainer: Douglas Lewan
;; Created: 2017 Nov 28
;; Version: 0.17
;; Keywords: files
;;; Commentary:
;; This file contains code for dealing with mode bits in cpio-mode.
;;; Documentation:
;;; Code:
;;
;; Dependencies
;;
(eval-when-compile
(require 'cl-lib)
(require 'cl-extra))
;;;;;;;;;;;;;;;;
;; Things to make the byte compiler happy.
(declare-function cpio-mode-value "cpio-mode.el")
;; EO things for the byte compiler.
;;;;;;;;;;;;;;;;
;;
;; Vars
;;
;;
;; Mode-related bits (adapted from /usr/include/linux/stat.h).
;;
(defconst s-ifunk #o1000000)
(defconst s-ifmt #o0170000)
(defconst s-ifsock #o0140000)
(defconst s-iflnk #o0120000)
(defconst s-ifreg #o0100000)
(defconst s-ifblk #o0060000)
(defconst s-ifdir #o0040000)
(defconst s-ifchr #o0020000)
(defconst s-ififo #o0010000)
(defconst s-isuid #o0004000)
(defconst s-isgid #o0002000)
(defconst s-isvtx #o0001000)
(defconst s-irwxu #o00700)
(defconst s-irusr #o00400)
(defconst s-iwusr #o00200)
(defconst s-ixusr #o00100)
(defconst s-irwxg #o00070)
(defconst s-irgrp #o00040)
(defconst s-iwgrp #o00020)
(defconst s-ixgrp #o00010)
(defconst s-irwxo #o00007)
(defconst s-iroth #o00004)
(defconst s-iwoth #o00002)
(defconst s-ixoth #o00001)
(defconst UNUSED-*cpio-low-mode-bits* (logior s-irwxu s-irwxg s-irwxo s-isuid s-isgid s-isvtx)
"A bit mask of the modes that can be set by chmod(1).")
;;
;; Library
;;
(defun s-islnk (m)
(= (logand m s-ifmt) s-iflnk))
(defun s-isreg (m)
(= (logand m s-ifmt) s-ifreg))
(defun s-isdir (m)
(= (logand m s-ifmt) s-ifdir))
(defun s-ischr (m)
(= (logand m s-ifmt) s-ifchr))
(defun s-isblk (m)
(= (logand m s-ifmt) s-ifblk))
(defun s-isfifo (m)
(= (logand m s-ifmt) s-ififo))
(defun s-issock (m)
(= (logand m s-ifmt) s-ifsock))
(defun cpio-special-file (attrs)
"Return non-NIL if the mode in ATTRS is as special file:
fmt, sock, link, block, character, fifo."
(let ((fname "cpio-special-file")
(mode (cpio-mode-value attrs)))
(or (= s-ifmt (logand s-ifmt mode))
(= s-ifsock (logand s-ifsock mode))
(= s-iflnk (logand s-iflnk mode)) ;Does this really belong here? I'm writing this to support (cpio-crc-make-chksum). Do links' checksums get calculated?
(= s-ifblk (logand s-ifblk mode))
(= s-ifdir (logand s-ifdir mode)) ;Is a directory a special file? Again, this has to do with calculating a checksum.
(= s-ifchr (logand s-ifchr mode))
(= s-ififo (logand s-ififo mode)))))
(defun cpio-int-mode-to-mode-string (int-mode)
"Convert an integer mode value to the corresponding ls -l version."
(let ((fname "cpio-int-mode-to-mode-string")
(file-type (cpio-int-mode-to-file-type int-mode))
(user-mode (cpio-int-mode-to-user-permissions int-mode))
(group-mode (cpio-int-mode-to-group-permissions int-mode))
(other-mode (cpio-int-mode-to-other-permissions int-mode)))
(concat file-type user-mode group-mode other-mode)))
(defvar *cpio-modes-link* "l")
(setq *cpio-modes-link* "l")
(defvar *cpio-modes-reg* "-")
(setq *cpio-modes-reg* "-")
(defvar *cpio-modes-dir* "d")
(setq *cpio-modes-dir* "d")
(defvar *cpio-modes-char* "c")
(setq *cpio-modes-char* "c")
(defvar *cpio-modes-block* "b")
(setq *cpio-modes-block* "b")
(defvar *cpio-modes-fifo* "p")
(setq *cpio-modes-fifo* "p")
(defvar *cpio-modes-sock* "s")
(setq *cpio-modes-sock* "s")
(defvar *cpio-modes-unknown* "?")
(setq *cpio-modes-unknown* "?")
(defun cpio-int-mode-to-file-type (int-mode)
"Extract the one character string that expresses the file type from INT-MODE.
CAUTION: Some file types are not present here:
D -- Solaris door
M -- Cray DMF migrated file
n -- HP-UX network special file
P -- Solaris port.
If you have access to any of those operating systems,
please let me know."
(let ((fname "cpio-int-mode-to-file-type"))
(cond ((s-islnk int-mode)
*cpio-modes-link*)
((s-isreg int-mode)
*cpio-modes-reg*)
((s-isdir int-mode)
*cpio-modes-dir*)
((s-ischr int-mode)
*cpio-modes-char*)
((s-isblk int-mode)
*cpio-modes-block*)
((s-isfifo int-mode)
*cpio-modes-fifo*)
((s-issock int-mode)
*cpio-modes-sock*)
(t
*cpio-modes-unknown*))))
(defun cpio-int-mode-to-user-permissions (int-mode)
"Extract the 3-character string expressing the user permissions from INT-MODE."
(let ((fname "cpio-int-mode-to-user-permissions")
(read-string (cpio-int-mode-to-user-read-string int-mode))
(write-string (cpio-int-mode-to-user-write-string int-mode))
(execute-string (cpio-int-mode-to-user-execute-string int-mode)))
(concat read-string write-string execute-string)))
(defun cpio-int-mode-to-user-read-string (int-mode)
"Extract the 1-character string expressing the user read permissions from INT-MODE."
(let ((fname "cpio-int-mode-to-user-read-string"))
(cond ((/= (logand int-mode s-irusr) 0)
"r")
(t "-"))))
(defun cpio-int-mode-to-user-write-string (int-mode)
"Extract the 1-character string expressing the user write permissions from INT-MODE."
(let ((fname "cpio-int-mode-to-user-write-string"))
(cond ((/= (logand int-mode s-iwusr) 0)
"w")
(t "-"))))
(defun cpio-int-mode-to-user-execute-string (int-mode)
"Extract the 1-character string expressing the user execute permissions from INT-MODE."
(let ((fname "cpio-int-mode-to-user-execute-string"))
(cond ((/= (logand int-mode s-ixusr) 0)
(if (/= (logand int-mode s-isuid) 0)
"s"
"x"))
(t
(if (/= (logand int-mode s-isuid) 0)
"S"
"-")))))
(defun cpio-int-mode-to-group-permissions (int-mode)
"Extract the 3-character string expressing the group permissions from INT-MODE."
(let ((fname "cpio-int-mode-to-group-permissions")
(read-string (cpio-int-mode-to-group-read-string int-mode))
(write-string (cpio-int-mode-to-group-write-string int-mode))
(execute-string (cpio-int-mode-to-group-execute-string int-mode)))
(concat read-string write-string execute-string)))
(defun cpio-int-mode-to-group-read-string (int-mode)
"Extract the 1-character string expressing the group read permissions from INT-MODE."
(let ((fname "cpio-int-mode-to-group-read-string"))
(cond ((/= (logand s-irgrp int-mode) 0)
"r")
(t "-"))))
(defun cpio-int-mode-to-group-write-string (int-mode)
"Extract the 1-character string expressing the group write permissions from INT-MODE."
(let ((fname "cpio-int-mode-to-group-write-string"))
(cond ((/= (logand s-iwgrp int-mode) 0)
"w")
(t "-"))))
(defun cpio-int-mode-to-group-execute-string (int-mode)
"Extract the 1-character string expressing the group execute permissions from INT-MODE."
(let ((fname "cpio-int-mode-to-group-execute-string"))
(cond ((/= (logand int-mode s-ixgrp) 0)
(if (/= (logand int-mode s-isgid) 0)
"s"
"x"))
(t
(if (/= (logand int-mode s-isgid) 0)
"S"
"-")))))
(defun cpio-int-mode-to-other-permissions (int-mode)
"Extract the 3-character string expressing the other permissions from INT-MODE."
(let ((fname "cpio-int-mode-to-other-permissions")
(read-string (cpio-int-mode-to-other-read-string int-mode))
(write-string (cpio-int-mode-to-other-write-string int-mode))
(execute-string (cpio-int-mode-to-other-execute-string int-mode)))
(concat read-string write-string execute-string)))
(defun cpio-int-mode-to-other-read-string (int-mode)
"Extract the 1-character string expressing the other read permissions from INT-MODE."
(let ((fname "cpio-int-mode-to-other-read-string"))
(cond ((/= (logand s-iroth int-mode) 0)
"r")
(t "-"))))
(defun cpio-int-mode-to-other-write-string (int-mode)
"Extract the 1-character string expressing the other write permissions from INT-MODE."
(let ((fname "cpio-int-mode-to-other-write-string"))
(cond ((/= (logand s-iwoth int-mode) 0)
"w")
(t "-"))))
(defun cpio-int-mode-to-other-execute-string (int-mode)
"Extract the 1-character string expressing the other execute permissions from INT-MODE."
(let ((fname "cpio-int-mode-to-other-execute-string"))
(cond ((/= (logand s-ixoth int-mode) 0)
(if (/= (logand s-isvtx int-mode) 0)
"t"
"x"))
(t
(if (/= (logand s-isvtx int-mode) 0)
"T"
"-")))))
(defun cpio-mode-string-to-int-mode (mode-string)
;; HEREHERE This should do some error checking.
;; It will currently flag an error if MODE-STRING is not long enough.
"Convert an ls -l style mode string to its corresponding integer."
(let* ((fname "cpio-mode-string-to-int-mode")
(bits 0)
(chars (mapcar 'string-to-char (split-string mode-string "" t)))
(type-char (car (cl-subseq chars 0 1)))
(owner-chars (cl-subseq chars 1 4))
(group-chars (cl-subseq chars 4 7))
(other-chars (cl-subseq chars 7 10)))
(setq bits (logior bits
(cpio-type-char-to-bits type-char)
(cpio-owner-chars-to-bits owner-chars)
(cpio-group-chars-to-bits group-chars)
(cpio-other-chars-to-bits other-chars)))
bits))
(defun cpio-type-char-to-bits (char)
"Return the mode bits implied by the given type CHAR."
(let ((fname "cpio-type-char-to-bits"))
(unless (and (characterp char)
(or (= char (string-to-char *cpio-modes-link*))
(= char (string-to-char *cpio-modes-reg*))
(= char (string-to-char *cpio-modes-dir*))
(= char (string-to-char *cpio-modes-char*))
(= char (string-to-char *cpio-modes-block*))
(= char (string-to-char *cpio-modes-fifo*))
(= char (string-to-char *cpio-modes-sock*))
(= char (string-to-char *cpio-modes-unknown*))))
(signal 'wrong-type-argument char))
(cond ((= char (string-to-char *cpio-modes-link*))
s-iflnk)
((= char (string-to-char *cpio-modes-reg*))
s-ifreg)
((= char (string-to-char *cpio-modes-dir*))
s-ifdir)
((= char (string-to-char *cpio-modes-char*))
s-ifchr)
((= char (string-to-char *cpio-modes-block*))
s-ifblk)
((= char (string-to-char *cpio-modes-fifo*))
s-ififo)
((= char (string-to-char *cpio-modes-sock*))
s-ifsock)
(t
(error "%s(): Uknown file type is not yet supported." fname)))))
(defun cpio-owner-chars-to-bits (chars)
"Interpret the given CHARS as bits relevant to the owner of a file."
(let ((fname "cpio-owner-chars-to-bits")
(read-char)
(write-char)
(exec-char)
(bits 0))
(unless (and (listp chars)
(= (length chars) 3)
(member (setq read-char (nth 0 chars)) '(?- ?r))
(member (setq write-char (nth 1 chars)) '(?- ?w))
(member (setq exec-char (nth 2 chars)) '(?- ?x ?s ?S)))
(signal 'wrong-type-argument chars))
(cond ((= read-char ?-))
((= read-char ?r)
(setq bits (logior bits s-irusr))))
(cond ((= write-char ?-))
((= write-char ?w)
(setq bits (logior bits s-iwusr))))
(cond ((= exec-char ?-))
((= exec-char ?x)
(setq bits (logior bits s-ixusr)))
((= exec-char ?s)
(setq bits (logior bits s-ixusr s-isuid)))
((= exec-char ?S)
(setq bits (logior bits s-isuid))))
bits))
(defun cpio-group-chars-to-bits (chars)
"Interpret CHARS as group mode bits."
(let ((fname "cpio-group-chars-to-bits")
(read-char)
(write-char)
(exec-char)
(bits 0))
(unless (and (listp chars)
(= (length chars) 3)
(member (setq read-char (nth 0 chars)) '(?- ?r))
(member (setq write-char (nth 1 chars)) '(?- ?w))
(member (setq exec-char (nth 2 chars)) '(?- ?x ?s ?S)))
(signal 'wrong-type-argument chars))
(cond ((= read-char ?-))
((= read-char ?r)
(setq bits (logior bits s-irgrp))))
(cond ((= write-char ?-))
((= write-char ?w)
(setq bits (logior bits s-iwgrp))))
(cond ((= exec-char ?-))
((= exec-char ?x)
(setq bits (logior bits s-ixgrp)))
((= exec-char ?s)
(setq bits (logior bits s-ixgrp s-isgid)))
((= exec-char ?S)
(setq bits (logior bits s-isgid))))
bits))
(defun cpio-other-chars-to-bits (chars)
"Interpret CHARS as other mode bits."
(let ((fname "cpio-other-chars-to-bits")
(read-char)
(write-char)
(exec-char)
(bits 0))
(unless (and (listp chars)
(= (length chars) 3)
(member (setq read-char (nth 0 chars)) '(?- ?r))
(member (setq write-char (nth 1 chars)) '(?- ?w))
(member (setq exec-char (nth 2 chars)) '(?- ?x ?t ?T)))
(signal 'wrong-type-argument chars))
(cond ((= read-char ?-))
((= read-char ?r)
(setq bits (logior bits s-iroth))))
(cond ((= write-char ?-))
((= write-char ?w)
(setq bits (logior bits s-iwoth))))
(cond ((= exec-char ?-))
((= exec-char ?x)
(setq bits (logior bits s-ixoth)))
((= exec-char ?t)
(setq bits (logior bits s-ixoth s-isvtx)))
((= exec-char ?T)
(setq bits (logior bits s-isvtx))))
bits))
(defun cpio-mode-extractable-p (mode)
"Return non-NIL if MODE represents an entry that can be extracted by cpio-mode.
That is, a regular file, symbolic link or directory. "
(let ((fname "cpio-mode-not-extractable-p"))
(or (s-islnk mode)
(s-isreg mode)
(s-isdir mode))))
(defun cpio-valid-numeric-mode (proposed-mode-num)
"Return non-NIL if the PROPOSED-MODE-NUM is a valid numeric file mode."
(let ((fname "cpio-valid-numeric-mode"))
(/= 0 (logxor proposed-mode-num
;; This list could be pared down a little,
;; but this is more readable.
(logand s-ifmt
s-ifsock
s-iflnk
s-ifreg
s-ifblk
s-ifdir
s-ifchr
s-ififo
s-isuid
s-isgid
s-isvtx
s-irwxu
s-irwxg
s-irwxo)))))
(provide 'cpio-modes)
;;; cpio-modes ends here