Skip to content

Commit

Permalink
Merge pull request Raynes#97 from chartbeat-labs/master
Browse files Browse the repository at this point in the history
Maintain file permissions in untar, and support octal chmod format
  • Loading branch information
Raynes committed Jan 14, 2016
2 parents 2600916 + 9ebd087 commit c6f1de6
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 12 deletions.
52 changes: 41 additions & 11 deletions src/me/raynes/fs.clj
Expand Up @@ -418,13 +418,41 @@
(.setLastModified f (or time (System/currentTimeMillis))))
f))

(defn- char-to-int
[c]
(- (int c) 48))

(defn- chmod-octal-digit
[f i user?]
(if (> i 7)
(throw (IllegalArgumentException. "Bad mode"))
(do (.setReadable f (pos? (bit-and i 4)) user?)
(.setWritable f (pos? (bit-and i 2)) user?)
(.setExecutable f (pos? (bit-and i 1)) user?))))

(defn- chmod-octal
[mode path]
(let [[user group world] (map char-to-int mode)
f (file path)]
(if (not= group world)
(throw (IllegalArgumentException.
"Bad mode. Group permissions must be equal to world permissions"))
(do (chmod-octal-digit f world false)
(chmod-octal-digit f user true)
path))))

(defn chmod
"Change file permissions. Returns path.
`mode` can be any combination of `r` (readable) `w` (writable) and
`mode` can be a permissions string in octal or symbolic format.
Symbolic: any combination of `r` (readable) `w` (writable) and
`x` (executable). It should be prefixed with `+` to set or `-` to
unset. And optional prefix of `u` causes the permissions to be set
for the owner only.
Octal: a string of three octal digits representing user, group, and
world permissions. The three bits of each digit signify read, write,
and execute permissions (in order of significance). Note that group
and world permissions must be equal.
Examples:
Expand All @@ -434,16 +462,18 @@
```"
[mode path]
(assert-exists path)
(let [[_ u op permissions] (re-find #"^(u?)([+-])([rwx]{1,3})$" mode)]
(when (nil? op) (throw (IllegalArgumentException. "Bad mode")))
(let [perm-set (set permissions)
f (file path)
flag (= op "+")
user (not (empty? u))]
(when (perm-set \r) (.setReadable f flag user))
(when (perm-set \w) (.setWritable f flag user))
(when (perm-set \x) (.setExecutable f flag user)))
path))
(if (re-matches #"^\d{3}$" mode)
(chmod-octal mode path)
(let [[_ u op permissions] (re-find #"^(u?)([+-])([rwx]{1,3})$" mode)]
(when (nil? op) (throw (IllegalArgumentException. "Bad mode")))
(let [perm-set (set permissions)
f (file path)
flag (= op "+")
user (not (empty? u))]
(when (perm-set \r) (.setReadable f flag user))
(when (perm-set \w) (.setWritable f flag user))
(when (perm-set \x) (.setExecutable f flag user)))
path)))

(defn copy+
"Copy `src` to `dest`, create directories if needed."
Expand Down
6 changes: 5 additions & 1 deletion src/me/raynes/fs/compression.clj
Expand Up @@ -79,7 +79,11 @@
(doseq [^TarArchiveEntry entry (tar-entries tin) :when (not (.isDirectory entry))
:let [output-file (fs/file target (.getName entry))]]
(fs/mkdirs (fs/parent output-file))
(io/copy tin output-file)))))
(io/copy tin output-file)
(when (.isFile entry)
(fs/chmod (apply str (take-last
3 (format "%05o" (.getMode entry))))
(.getPath output-file)))))))

(defn gunzip
"Takes a path to a gzip file `source` and unzips it."
Expand Down
14 changes: 14 additions & 0 deletions test/me/raynes/core_test.clj
Expand Up @@ -191,6 +191,20 @@
(executable? f) => false)
(delete f)))

(fact
(let [f (temp-file "fs-")]
(chmod "777" f)
(executable? f) => true
(readable? f) => true
(writeable? f) => true
(chmod "000" f)
(when-not (re-find #"Windows" (System/getProperty "os.name"))
(chmod "-x" f)
(executable? f) => false
(readable? f) => false
(writeable? f) => false)
(delete f)))

(fact
(let [from (create-walk-dir)
to (temp-dir "fs-")
Expand Down

0 comments on commit c6f1de6

Please sign in to comment.