summaryrefslogtreecommitdiff
path: root/src/cstore/unionfs/cstore-unionfs.hpp
blob: fd145ad939af6c1dc677e266175e4e41cc36b2a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
/*
 * 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 <http://www.gnu.org/licenses/>.
 */

#ifndef _CSTORE_UNIONFS_H_
#define _CSTORE_UNIONFS_H_
#include <vector>
#include <string>

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <boost/filesystem.hpp>

#include <cli_cstore.h>
#include <cstore/cstore.hpp>
#include <cstore/unionfs/fspath.hpp>

// forward decl
namespace commit {
class PrioNode;
}
using namespace boost::filesystem;

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_MARKER_UNIONFS;
  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;
  };
  unique_ptr<SavePaths> create_save_paths() {
    return unique_ptr<SavePaths>(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<string>& cnodes, bool active_cfg);
  void get_all_tmpl_child_node_names(vector<string>& cnodes) {
    get_all_child_dir_names(tmpl_path, cnodes);
  };
  bool write_value_vec(const vector<string>& 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<string>& 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<string> *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<string>& 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;
    file_status s = status(path, ec);
    fs = s;
    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_ */