diff options
author | David Barksdale <amatus.amongus@gmail.com> | 2012-06-13 21:01:35 -0500 |
---|---|---|
committer | David Barksdale <amatus.amongus@gmail.com> | 2012-06-13 21:02:24 -0500 |
commit | 7f1ab14907f95a81055bf113d0c9a2a66f149c0c (patch) | |
tree | 6264ee850aa5923a856ab02e95c9eb7e25e78cda /src | |
parent | b1a30b1eef2bab695621cd7f2fda600f890b13dd (diff) |
The start of work on the next phase: the local backend.
Diffstat (limited to 'src')
-rw-r--r-- | src/clojure/foofs/localbackend.clj | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/src/clojure/foofs/localbackend.clj b/src/clojure/foofs/localbackend.clj new file mode 100644 index 0000000..a00560b --- /dev/null +++ b/src/clojure/foofs/localbackend.clj @@ -0,0 +1,356 @@ +; Copyright (C) David Barksdale 2012 <amatus.amongus@gmail.com> +; +; foofs 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. +; +; foofs 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 <http://www.gnu.org/licenses/>. + +(ns foofs.localbackend + (:use [foofs.filesystembackend :only [FilesystemBackend]] + [foofs.fuse bytebuffer jna] + foofs.util)) + +(def empty-inode + {:size 0 + :blocks 0 + :atime 0 + :mtime 0 + :ctime 0 + :atimensec 0 + :mtimensec 0 + :ctimensec 0 + :mode 0 + :nlink 0 + :uid 0 + :gid 0 + :rdev 0}) + +;; this is obviously a state monadic function +(defn make-inode + [state mode] + (let [child-nodeid (next-key (:inode-table state) + (:next-nodeid state) + Long/MIN_VALUE Long/MAX_VALUE) + inode (assoc empty-inode :mode mode) + state (assoc-in state [:inode-table child-nodeid] inode) + state (assoc state :next-nodeid (inc child-nodeid))] + [state child-nodeid])) + +(defn inode-modifier! + [state-agent nodeid f continuation!] + (send + state-agent + (fn [state] + (let [inode-table (:inode-table state) + inode (get inode-table nodeid)] + (if (nil? inode) + (do (continuation! errno-noent) state) + (let [new-inode (f inode)] + (agent-do state-agent (continuation! new-inode)) + (assoc-in state [:inode-table nodeid] new-inode))))))) + +(defn attribute-modifier! + [state-agent nodeid f attribute continuation!] + (inode-modifier! state-agent nodeid + (fn [inode] + (assoc inode attribute (f (get inode attribute)))) + continuation!)) + +;; TODO: Implement CHK encryption, Reed-Solomon coding, and store the blocks +;; in local files. +(defrecord LocalBackend + [^clojure.lang.Agent state-agent] + FilesystemBackend + (lookup [_ nodeid child continuation!] + (let [lookup-table (:lookup-table (deref state-agent))] + (if (= "" child) + (continuation! nodeid) + (continuation! (get-in lookup-table [nodeid child]))))) + (getattr [_ nodeid continuation!] + (let [inode (get-in (deref state-agent) [:inode-table nodeid])] + (if (nil? inode) + (continuation! nil) + (continuation! (assoc inode :nodeid nodeid))))) + (reference [_ nodeid continuation!] + (attribute-modifier! state-agent nodeid inc :nlink continuation!)) + (dereference [_ nodeid continuation!] + (attribute-modifier! state-agent nodeid dec :nlink continuation!)) + (clonedir [_ nodeid continuation!] + (let [state (deref state-agent) + inode-table (:inode-table state)] + (continuation! + (map + (fn [[filename child-nodeid]] + {:name filename + :nodeid child-nodeid + :type (get-in inode-table [child-nodeid :mode])}) + (get-in state [:lookup-table nodeid]))))) + (readfile [_ nodeid offset size continuation!] + (let [file (get-in (deref state-agent) [:file-table nodeid])] + (if (nil? file) + (continuation! errno-noent) + (continuation! (take size (drop offset file)))))) + (writefile [_ nodeid offset size data continuation!] + (send + state-agent + (fn [state] + (let [inode-table (:inode-table state) + file-table (:file-table state) + inode (get inode-table nodeid) + file (get file-table nodeid)] + (if (nil? inode) + (do (continuation! errno-noent) state) + (let [tail-offset (+ offset size) + tail-size (- (max tail-offset (:size inode)) tail-offset) + file-extended (concat file (repeat (byte 0))) + file-written (concat + (take offset file-extended) + (buffer-seq! data) + (take tail-size + (drop tail-offset file-extended))) + new-size (count file-written) + state (assoc-in state [:inode-table nodeid :size] new-size) + state (assoc-in state [:file-table nodeid] file-written)] + (agent-do state-agent + (continuation! {:size (.limit data)})) + state)))))) + (mknod [_ nodeid filename mode continuation!] + (send + state-agent + (fn [state] + (if (contains? (get-in state [:lookup-table nodeid]) filename) + (do (continuation! errno-exist) state) + (let [[state child-nodeid] (make-inode state mode) + state (assoc-in state [:inode-table child-nodeid :nlink] 1) + state (assoc-in state [:lookup-table nodeid filename] + child-nodeid) + state (assoc state :next-nodeid (inc child-nodeid)) + inode (get-in state [:inode-table child-nodeid])] + (agent-do state-agent + (continuation! (assoc inode :nodeid child-nodeid))) + state))))) + (mkdir [_ nodeid filename mode continuation!] + (send + state-agent + (fn [state] + (if (contains? (get-in state [:lookup-table nodeid]) filename) + (do (continuation! errno-exist) state) + (let [[state child-nodeid] (make-inode + state (bit-or stat-type-directory mode)) + state (assoc-in state [:inode-table child-nodeid :nlink] 2) + state (assoc-in state [:lookup-table nodeid filename] + child-nodeid) + state (assoc-in state [:lookup-table child-nodeid] + {"." child-nodeid ".." nodeid}) + nlink (get-in state [:inode-table nodeid :nlink]) + state (assoc-in state [:inode-table nodeid :nlink] + (inc nlink)) + state (assoc state :next-nodeid (inc child-nodeid)) + inode (get-in state [:inode-table child-nodeid])] + (agent-do state-agent + (continuation! (assoc inode :nodeid child-nodeid))) + state))))) + (link [_ nodeid filename target-nodeid continuation!] + (send + state-agent + (fn [state] + (let [children (get-in state [:lookup-table nodeid]) + inode (get-in state [:inode-table nodeid]) + target-inode (get-in state [:inode-table target-nodeid])] + (if (contains? children filename) + (do (continuation! errno-exist) state) + (if (not (= stat-type-directory + (bit-and stat-type-mask (:mode inode)))) + (do (continuation! errno-notdir) state) + (if (nil? target-inode) + (do (continuation! errno-noent) state) + (let [state (assoc-in state [:inode-table target-nodeid :nlink] + (inc (:nlink target-inode))) + state (assoc-in state [:lookup-table nodeid filename] + target-nodeid)] + (agent-do state-agent + (continuation! (assoc target-inode + :nodeid target-nodeid))) + state)))))))) + (unlink [_ nodeid filename continuation!] + (send + state-agent + (fn [state] + (let [inode-table (:inode-table state) + inode (get inode-table nodeid) + children (get-in state [:lookup-table nodeid]) + child-nodeid (get children filename) + child-inode (get inode-table child-nodeid)] + (cond + (nil? inode) + (do (continuation! errno-noent) state) + (not (= stat-type-directory + (bit-and stat-type-mask (:mode inode)))) + (do (continuation! errno-notdir) state) + (nil? child-inode) + (do (continuation! errno-noent) state) + (= stat-type-directory + (bit-and stat-type-mask (:mode child-inode))) + (do (continuation! errno-isdir) state) + true + (let [nlink (dec (:nlink child-inode)) + state (assoc-in state [:lookup-table nodeid] + (dissoc children filename)) + inode-table (if (zero? nlink) + (dissoc inode-table child-nodeid) + (assoc-in inode-table [child-nodeid :nlink] + nlink)) + state (assoc state :inode-table inode-table)] + ;; agent-do? + (continuation! 0) + state)))))) + (rmdir [_ nodeid filename continuation!] + (send + state-agent + (fn [state] + (let [inode-table (:inode-table state) + inode (get inode-table nodeid) + lookup-table (:lookup-table state) + children (get lookup-table nodeid) + child-nodeid (get children filename) + child-inode (get inode-table child-nodeid) + child-children (get lookup-table child-nodeid)] + (cond + (nil? inode) + (do (continuation! errno-noent) state) + (not (= stat-type-directory + (bit-and stat-type-mask (:mode inode)))) + (do (continuation! errno-notdir) state) + (nil? child-nodeid) + (do (continuation! errno-noent) state) + (not (= stat-type-directory + (bit-and stat-type-mask (:mode child-inode)))) + (do (continuation! errno-notdir) state) + (not (empty? (dissoc child-children "." ".."))) + (do (continuation! errno-notempty) state) + true + (let [nlink (- (:nlink child-inode) 2) + lookup-table (assoc lookup-table nodeid + (dissoc children filename)) + lookup-table (dissoc lookup-table child-nodeid) + state (assoc state :lookup-table lookup-table) + inode-table (if (zero? nlink) + (dissoc inode-table child-nodeid) + (assoc-in inode-table [child-nodeid :nlink] + nlink)) + inode-table (assoc-in inode-table [nodeid :nlink] + (dec (get-in inode-table + [nodeid :nlink]))) + state (assoc state :inode-table inode-table)] + (agent-do state-agent (continuation! 0)) + state)))))) + (chmod [_ nodeid mode continuation!] + (attribute-modifier! state-agent nodeid + #(bit-or (bit-and stat-type-mask %) + (bit-and stat-mode-mask mode)) + :mode continuation!)) + (setuid [_ nodeid uid continuation!] + (attribute-modifier! state-agent nodeid + (fn [_] uid) + :uid continuation!)) + (setgid [_ nodeid gid continuation!] + (attribute-modifier! state-agent nodeid + (fn [_] gid) + :gid continuation!)) + (truncate [_ nodeid size continuation!] + (send + state-agent + (fn [state] + (if (nil? (get-in state [:inode-table nodeid])) + (do (continuation! errno-noent) state) + (let [file (get-in state [:file-table nodeid]) + state (assoc-in state [:inode-table nodeid :size] size) + state (assoc-in state [:file-table nodeid] + (take size (concat file (repeat (byte 0)))))] + (agent-do state-agent (continuation! nil)) + state))))) + (setatime [_ nodeid seconds nseconds continuation!] + (inode-modifier! state-agent nodeid + #(assoc % :atime seconds :atimensec nseconds) + continuation!)) + (setmtime [_ nodeid seconds nseconds continuation!] + (inode-modifier! state-agent nodeid + #(assoc % :mtime seconds :mtimensec nseconds) + continuation!)) + (rename [_ nodeid target-nodeid filename target-filename continuation!] + (send + state-agent + (fn [state] + (let [inode-table (:inode-table state) + lookup-table (:lookup-table state) + dir-inode (get inode-table nodeid) + target-dir-inode (get inode-table target-nodeid) + children (get lookup-table nodeid) + file-nodeid (get children filename) + file-inode (get inode-table file-nodeid)] + (cond + (not (= stat-type-directory + (bit-and stat-type-mask (:mode dir-inode)))) + (do (continuation! errno-notdir) state) + (not (= stat-type-directory + (bit-and stat-type-mask (:mode target-dir-inode)))) + (do (continuation! errno-notdir) state) + (nil? file-inode) + (do (continuation! errno-noent) state) + true + (let [lookup-table (assoc lookup-table nodeid + (dissoc children filename)) + lookup-table (assoc-in lookup-table [target-nodeid + target-filename] + file-nodeid) + ;; If we are renaming a directory fixup ".." link + file-isdir (= stat-type-directory + (bit-and stat-type-mask (:mode file-inode))) + lookup-table (if file-isdir + (assoc-in lookup-table [file-nodeid + ".."] + target-nodeid) + lookup-table) + inode-table (if file-isdir + (assoc-in inode-table [nodeid :nlink] + (dec (:nlink dir-inode))) + inode-table) + inode-table (if file-isdir + (assoc-in inode-table [target-nodeid :nlink] + (inc (:nlink target-dir-inode))) + inode-table) + state (assoc state :inode-table inode-table + :lookup-table lookup-table)] + (agent-do state-agent (continuation! nil)) + state)))))) + (symlink [_ nodeid filename link-target continuation!] + (send + state-agent + (fn [state] + (if (contains? (get-in state [:lookup-table nodeid]) filename) + (do (continuation! errno-exist) state) + (let [[state child-nodeid] (make-inode + state + (bit-or stat-type-link 0777)) + state (assoc-in state [:inode-table child-nodeid :nlink] 1) + state (assoc-in state [:lookup-table nodeid filename] + child-nodeid) + state (assoc state :next-nodeid (inc child-nodeid)) + state (assoc-in state [:file-table child-nodeid] link-target) + inode (get-in state [:inode-table child-nodeid])] + (agent-do state-agent + (continuation! (assoc inode :nodeid child-nodeid))) + state))))) + (readlink [_ nodeid continuation!] + (let [file (get-in (deref state-agent) [:file-table nodeid])] + (if (nil? file) + (continuation! errno-noent) + (continuation! file))))) |