/* * Copyright (C) 2010 Vyatta, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * 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 . */ #ifndef _CSTORE_UNIONFS_H_ #define _CSTORE_UNIONFS_H_ #include #include #include #include #include #include #include #include #include #include // forward decl namespace commit { class PrioNode; } namespace cstore { // begin namespace cstore namespace unionfs { // begin namespace unionfs namespace b_fs = boost::filesystem; namespace b_s = boost::system; class UnionfsCstore : public Cstore { public: UnionfsCstore(bool use_edit_level); UnionfsCstore(const string& session_id, string& env); virtual ~UnionfsCstore(); ////// public virtual functions declared in base class bool markSessionUnsaved(); bool unmarkSessionUnsaved(); bool sessionUnsaved(); bool sessionChanged(); bool setupSession(); bool teardownSession(); bool inSession(); bool clearCommittedMarkers(); bool commitConfig(commit::PrioNode& pnode); bool getCommitLock(); private: // constants static const string C_ENV_TMPL_ROOT; static const string C_ENV_WORK_ROOT; static const string C_ENV_ACTIVE_ROOT; static const string C_ENV_CHANGE_ROOT; static const string C_ENV_TMP_ROOT; static const string C_DEF_TMPL_ROOT; static const string C_DEF_CFG_ROOT; static const string C_DEF_ACTIVE_ROOT; static const string C_DEF_CHANGE_PREFIX; static const string C_DEF_WORK_PREFIX; static const string C_DEF_TMP_PREFIX; static const string C_MARKER_DEF_VALUE; static const string C_MARKER_DEACTIVATE; static const string C_MARKER_CHANGED; static const string C_MARKER_UNSAVED; static const string C_COMMITTED_MARKER_FILE; static const string C_COMMENT_FILE; static const string C_TAG_NAME; static const string C_VAL_NAME; static const string C_DEF_NAME; static const string C_COMMIT_LOCK_FILE; /* max size for a file. * currently this includes value file and comment file. */ static const size_t C_UNIONFS_MAX_FILE_SIZE = 262144; // root dirs (constant) FsPath work_root; // working root (union) FsPath active_root; // active root (readonly part of union) FsPath change_root; // change root (r/w part of union) FsPath tmp_root; // temp root FsPath tmpl_root; // template root // path buffers FsPath mutable_cfg_path; // mutable part of config path FsPath tmpl_path; // whole template path FsPath orig_mutable_cfg_path; // original mutable cfg path FsPath orig_tmpl_path; // original template path // for commit processing FsPath tmp_active_root; FsPath tmp_work_root; FsPath commit_marker_file; void init_commit_data() { tmp_active_root = tmp_root; tmp_work_root = tmp_root; commit_marker_file = tmp_root; tmp_active_root.push("active"); tmp_work_root.push("work"); commit_marker_file.push(C_COMMITTED_MARKER_FILE); } bool construct_commit_active(commit::PrioNode& node); bool mark_dir_changed(const FsPath& d, const FsPath& root); bool sync_dir(const FsPath& src, const FsPath& dst, const FsPath& root); ////// virtual functions defined in base class // begin path modifiers void push_tmpl_path(const char *new_comp) { push_path(tmpl_path, new_comp); }; void push_tmpl_path_tag() { /* not using push_path => not "escaped". * NOTE: this is the only interface that can push tmpl_path "unescaped". * this means the pop_tmpl_path() function potentially may return * incorrect value if used in combination with this function. * however, since current C_TAG_NAME doesn't contain any escape * sequences, this cannot happen for now. */ tmpl_path.push(C_TAG_NAME); }; void pop_tmpl_path() { pop_path(tmpl_path); }; void pop_tmpl_path(string& last) { pop_path(tmpl_path, last); }; void push_cfg_path(const char *new_comp) { push_path(mutable_cfg_path, new_comp); }; void pop_cfg_path() { pop_path(mutable_cfg_path); }; void pop_cfg_path(string& last) { pop_path(mutable_cfg_path, last); }; void append_cfg_path(const Cpath& path_comps) { for (size_t i = 0; i < path_comps.size(); i++) { push_cfg_path(path_comps[i]); } }; void reset_paths(bool to_root=false) { if (to_root){ char *val; if ((val = getenv(C_ENV_TMPL_ROOT.c_str()))) { tmpl_path = val; } else { tmpl_path = C_DEF_TMPL_ROOT; } mutable_cfg_path = "/"; } else { tmpl_path = orig_tmpl_path; mutable_cfg_path = orig_mutable_cfg_path; } }; class UnionfsSavePaths : public SavePaths { public: UnionfsSavePaths(UnionfsCstore *cs) : cstore(cs), cpath(cs->mutable_cfg_path), tpath(cs->tmpl_path) {}; ~UnionfsSavePaths() { cstore->mutable_cfg_path = cpath; cstore->tmpl_path = tpath; }; private: UnionfsCstore *cstore; FsPath cpath; FsPath tpath; }; auto_ptr create_save_paths() { return auto_ptr(new UnionfsSavePaths(this)); }; bool cfg_path_at_root() { return (!mutable_cfg_path.has_parent_path()); }; bool tmpl_path_at_root() { return (tmpl_path == tmpl_root); }; // end path modifiers // these operate on current tmpl path bool tmpl_node_exists(); Ctemplate *tmpl_parse(); // these operate on current work path bool add_node(); bool remove_node(); void get_all_child_node_names_impl(vector& cnodes, bool active_cfg); void get_all_tmpl_child_node_names(vector& cnodes) { get_all_child_dir_names(tmpl_path, cnodes); }; bool write_value_vec(const vector& vvec, bool active_cfg); bool rename_child_node(const char *oname, const char *nname); bool copy_child_node(const char *oname, const char *nname); bool mark_display_default(); bool unmark_display_default(); bool mark_deactivated(); bool unmark_deactivated(); bool unmark_deactivated_descendants(); bool mark_changed_with_ancestors(); bool unmark_changed_with_descendants(); bool remove_comment(); bool set_comment(const string& comment); bool discard_changes(unsigned long long& num_removed); // observers for work path bool cfg_node_changed(); // observers for work path or active path bool cfg_node_exists(bool active_cfg); bool read_value_vec(vector& vvec, bool active_cfg); bool marked_deactivated(bool active_cfg); bool get_comment(string& comment, bool active_cfg); bool marked_display_default(bool active_cfg); // observers for "edit/tmpl levels" (for "edit"-related operations). // note that these should be moved to base class in the future. string get_edit_level_path() { return cfg_path_to_str(); }; string get_tmpl_level_path() { return tmpl_path_to_str(); }; void get_edit_level(Cpath& path_comps); bool edit_level_at_root() { return cfg_path_at_root(); }; // functions for commit operation bool marked_committed(bool is_delete); bool mark_committed(bool is_delete); // for testing/debugging string cfg_path_to_str(); string tmpl_path_to_str(); ////// private functions FsPath get_work_path() { return (work_root / mutable_cfg_path); }; FsPath get_active_path() { return (active_root / mutable_cfg_path); }; FsPath get_change_path() { return (change_root / mutable_cfg_path); }; void push_path(FsPath& old_path, const char *new_comp); void pop_path(FsPath& path); void pop_path(FsPath& path, string& last); bool check_dir_entries(const FsPath& root, vector *cnodes, bool filter_nodes = true, bool empty_check = false); bool is_directory_empty(const FsPath& d) { return (!check_dir_entries(d, NULL, false, true)); } void get_all_child_dir_names(const FsPath& root, vector& nodes) { check_dir_entries(root, &nodes); } bool write_file(const char *file, const string& data, bool append = false); bool write_file(const FsPath& file, const string& data, bool append = false) { return write_file(file.path_cstr(), data, append); } bool create_file(const char *file) { return write_file(file, ""); }; bool create_file(const FsPath& file) { return write_file(file, ""); }; bool read_whole_file(const FsPath& file, string& data); void recursive_copy_dir(const FsPath& src, const FsPath& dst, bool filter_dot_entries = false); void get_committed_marker(bool is_delete, string& marker); bool find_line_in_file(const FsPath& file, const string& line); bool do_mount(const FsPath& rwdir, const FsPath& rdir, const FsPath& mdir); bool do_umount(const FsPath& mdir); // boost fs operations wrappers bool b_fs_get_file_status(const char *path, b_fs::file_status& fs) { b_s::error_code ec; fs = b_fs::detail::status_api(path, ec); return (!ec); }; bool path_exists(const char *path); bool path_exists(const FsPath& path) { return path_exists(path.path_cstr()); }; bool path_is_directory(const char *path); bool path_is_directory(const FsPath& path) { return path_is_directory(path.path_cstr()); }; bool path_is_regular(const char *path); bool path_is_regular(const FsPath& path) { return path_is_regular(path.path_cstr()); }; bool remove_dir_content(const char *path); }; } // end namespace unionfs } // end namespace cstore #endif /* _CSTORE_UNIONFS_H_ */