]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "include/int_types.h" | |
5 | #include "include/types.h" | |
6 | ||
7 | #include <unistd.h> | |
8 | #include <fcntl.h> | |
9 | #include <errno.h> | |
10 | #include <stdlib.h> | |
11 | #include <sys/types.h> | |
12 | #include <sys/stat.h> | |
13 | #include <sys/ioctl.h> | |
14 | ||
15 | #include "include/compat.h" | |
16 | #include "include/linux_fiemap.h" | |
17 | #include "include/color.h" | |
18 | #include "include/buffer.h" | |
19 | #include "include/assert.h" | |
20 | ||
21 | #include <iostream> | |
22 | #include <fstream> | |
23 | #include <sstream> | |
24 | ||
25 | #include "common/errno.h" | |
26 | #include "common/config.h" | |
27 | #include "common/sync_filesystem.h" | |
28 | ||
7c673cae FG |
29 | #include "ZFSFileStoreBackend.h" |
30 | ||
224ce89b | 31 | #define dout_context cct() |
7c673cae FG |
32 | #define dout_subsys ceph_subsys_filestore |
33 | #undef dout_prefix | |
34 | #define dout_prefix *_dout << "zfsfilestorebackend(" << get_basedir_path() << ") " | |
35 | ||
36 | ZFSFileStoreBackend::ZFSFileStoreBackend(FileStore *fs) : | |
37 | GenericFileStoreBackend(fs), base_zh(NULL), current_zh(NULL), | |
224ce89b | 38 | m_filestore_zfs_snap(cct()->_conf->filestore_zfs_snap) |
7c673cae FG |
39 | { |
40 | int ret = zfs.init(); | |
41 | if (ret < 0) { | |
42 | dout(0) << "ZFSFileStoreBackend: failed to init libzfs" << dendl; | |
43 | return; | |
44 | } | |
45 | ||
46 | base_zh = zfs.path_to_zhandle(get_basedir_path().c_str(), ZFS::TYPE_FILESYSTEM); | |
47 | if (!base_zh) { | |
48 | dout(0) << "ZFSFileStoreBackend: failed to get zfs handler for basedir" << dendl; | |
49 | return; | |
50 | } | |
51 | ||
52 | update_current_zh(); | |
53 | } | |
54 | ||
55 | ZFSFileStoreBackend::~ZFSFileStoreBackend() | |
56 | { | |
57 | if (base_zh) | |
58 | zfs.close(base_zh); | |
59 | if (current_zh) | |
60 | zfs.close(current_zh); | |
61 | } | |
62 | ||
63 | int ZFSFileStoreBackend::update_current_zh() | |
64 | { | |
65 | char path[PATH_MAX]; | |
66 | snprintf(path, sizeof(path), "%s/current", zfs.get_name(base_zh)); | |
67 | ZFS::Handle *zh = zfs.open(path, ZFS::TYPE_FILESYSTEM); | |
68 | if (zh) { | |
69 | char *mnt; | |
70 | if (zfs.is_mounted(zh, &mnt)) { | |
71 | int ret = get_current_path() == mnt; | |
72 | free(mnt); | |
73 | if (ret) { | |
74 | current_zh = zh; | |
75 | return 0; | |
76 | } | |
77 | } else { | |
78 | int ret = zfs.mount(zh, NULL, 0); | |
79 | if (ret < 0) { | |
80 | ret = -errno; | |
81 | dout(0) << "update_current_zh: zfs_mount '" << zfs.get_name(zh) | |
82 | << "' got " << cpp_strerror(ret) << dendl; | |
83 | return ret; | |
84 | } | |
85 | } | |
86 | zfs.close(zh); | |
87 | } else { | |
88 | dout(0) << "update_current_zh: zfs_open '" << path << "' got NULL" << dendl; | |
89 | return -ENOENT; | |
90 | } | |
91 | ||
92 | zh = zfs.path_to_zhandle(get_current_path().c_str(), ZFS::TYPE_FILESYSTEM); | |
93 | if (zh) { | |
94 | if (strcmp(zfs.get_name(base_zh), zfs.get_name(zh))) { | |
95 | current_zh = zh; | |
96 | return 0; | |
97 | } | |
98 | zfs.close(zh); | |
99 | dout(0) << "update_current_zh: basedir and current/ on the same filesystem" << dendl; | |
100 | } else { | |
101 | dout(0) << "update_current_zh: current/ not exist" << dendl; | |
102 | } | |
103 | return -ENOENT; | |
104 | } | |
105 | ||
106 | int ZFSFileStoreBackend::detect_features() | |
107 | { | |
108 | if (!current_zh) | |
109 | dout(0) << "detect_features: null zfs handle for current/" << dendl; | |
110 | return 0; | |
111 | } | |
112 | ||
113 | bool ZFSFileStoreBackend::can_checkpoint() | |
114 | { | |
115 | return m_filestore_zfs_snap && current_zh != NULL; | |
116 | } | |
117 | ||
118 | int ZFSFileStoreBackend::create_current() | |
119 | { | |
120 | struct stat st; | |
121 | int ret = ::stat(get_current_path().c_str(), &st); | |
122 | if (ret == 0) { | |
123 | // current/ exists | |
124 | if (!S_ISDIR(st.st_mode)) { | |
125 | dout(0) << "create_current: current/ exists but is not a directory" << dendl; | |
126 | return -ENOTDIR; | |
127 | } | |
128 | return 0; | |
129 | } else if (errno != ENOENT) { | |
130 | ret = -errno; | |
131 | dout(0) << "create_current: cannot stat current/ " << cpp_strerror(ret) << dendl; | |
132 | return ret; | |
133 | } | |
134 | ||
135 | char path[PATH_MAX]; | |
136 | snprintf(path, sizeof(path), "%s/current", zfs.get_name(base_zh)); | |
137 | ret = zfs.create(path, ZFS::TYPE_FILESYSTEM); | |
138 | if (ret < 0 && errno != EEXIST) { | |
139 | ret = -errno; | |
140 | dout(0) << "create_current: zfs_create '" << path << "' got " << cpp_strerror(ret) << dendl; | |
141 | return ret; | |
142 | } | |
143 | ||
144 | ret = update_current_zh(); | |
145 | return ret; | |
146 | } | |
147 | ||
148 | static int list_checkpoints_callback(ZFS::Handle *zh, void *data) | |
149 | { | |
150 | list<string> *ls = static_cast<list<string> *>(data); | |
151 | string str = ZFS::get_name(zh); | |
152 | size_t pos = str.find('@'); | |
153 | assert(pos != string::npos && pos + 1 != str.length()); | |
154 | ls->push_back(str.substr(pos + 1)); | |
155 | return 0; | |
156 | } | |
157 | ||
158 | int ZFSFileStoreBackend::list_checkpoints(list<string>& ls) | |
159 | { | |
160 | dout(10) << "list_checkpoints:" << dendl; | |
161 | if (!current_zh) | |
162 | return -EINVAL; | |
163 | ||
164 | list<string> snaps; | |
165 | int ret = zfs.iter_snapshots_sorted(current_zh, list_checkpoints_callback, &snaps); | |
166 | if (ret < 0) { | |
167 | ret = -errno; | |
168 | dout(0) << "list_checkpoints: zfs_iter_snapshots_sorted got" << cpp_strerror(ret) << dendl; | |
169 | return ret; | |
170 | } | |
171 | ls.swap(snaps); | |
172 | return 0; | |
173 | } | |
174 | ||
175 | int ZFSFileStoreBackend::create_checkpoint(const string& name, uint64_t *cid) | |
176 | { | |
177 | dout(10) << "create_checkpoint: '" << name << "'" << dendl; | |
178 | if (!current_zh) | |
179 | return -EINVAL; | |
180 | ||
181 | // looks like zfsonlinux doesn't flush dirty data when taking snapshot | |
182 | int ret = sync_filesystem(get_current_fd()); | |
183 | if (ret < 0) { | |
184 | ret = -errno; | |
185 | dout(0) << "create_checkpoint: sync_filesystem got" << cpp_strerror(ret) << dendl; | |
186 | return ret; | |
187 | } | |
188 | ||
189 | char path[PATH_MAX]; | |
190 | snprintf(path, sizeof(path), "%s@%s", zfs.get_name(current_zh), name.c_str()); | |
191 | ret = zfs.snapshot(path, false); | |
192 | if (ret < 0) { | |
193 | ret = -errno; | |
194 | dout(0) << "create_checkpoint: zfs_snapshot '" << path << "' got" << cpp_strerror(ret) << dendl; | |
195 | return ret; | |
196 | } | |
197 | if (cid) | |
198 | *cid = 0; | |
199 | return 0; | |
200 | } | |
201 | ||
202 | int ZFSFileStoreBackend::rollback_to(const string& name) | |
203 | { | |
204 | dout(10) << "rollback_to: '" << name << "'" << dendl; | |
205 | if (!current_zh) | |
206 | return -EINVAL; | |
207 | ||
208 | // umount current to avoid triggering online rollback deadlock | |
209 | int ret; | |
210 | if (zfs.is_mounted(current_zh, NULL)) { | |
211 | ret = zfs.umount(current_zh, NULL, 0); | |
212 | if (ret < 0) { | |
213 | ret = -errno; | |
214 | dout(0) << "rollback_to: zfs_umount '" << zfs.get_name(current_zh) << "' got" << cpp_strerror(ret) << dendl; | |
215 | } | |
216 | } | |
217 | ||
218 | char path[PATH_MAX]; | |
219 | snprintf(path, sizeof(path), "%s@%s", zfs.get_name(current_zh), name.c_str()); | |
220 | ||
221 | ZFS::Handle *snap_zh = zfs.open(path, ZFS::TYPE_SNAPSHOT); | |
222 | if (!snap_zh) { | |
223 | dout(0) << "rollback_to: zfs_open '" << path << "' got NULL" << dendl; | |
224 | return -ENOENT; | |
225 | } | |
226 | ||
227 | ret = zfs.rollback(current_zh, snap_zh, false); | |
228 | if (ret < 0) { | |
229 | ret = -errno; | |
230 | dout(0) << "rollback_to: zfs_rollback '" << zfs.get_name(snap_zh) << "' got" << cpp_strerror(ret) << dendl; | |
231 | } | |
232 | ||
233 | if (!zfs.is_mounted(current_zh, NULL)) { | |
234 | int ret = zfs.mount(current_zh, NULL, 0); | |
235 | if (ret < 0) { | |
236 | ret = -errno; | |
237 | dout(0) << "update_current_zh: zfs_mount '" << zfs.get_name(current_zh) << "' got " << cpp_strerror(ret) << dendl; | |
238 | return ret; | |
239 | } | |
240 | } | |
241 | ||
242 | zfs.close(snap_zh); | |
243 | return ret; | |
244 | } | |
245 | ||
246 | int ZFSFileStoreBackend::destroy_checkpoint(const string& name) | |
247 | { | |
248 | dout(10) << "destroy_checkpoint: '" << name << "'" << dendl; | |
249 | if (!current_zh) | |
250 | return -EINVAL; | |
251 | ||
252 | int ret = zfs.destroy_snaps(current_zh, name.c_str(), true); | |
253 | if (ret < 0) { | |
254 | ret = -errno; | |
255 | dout(0) << "destroy_checkpoint: zfs_destroy_snaps '" << name << "' got" << cpp_strerror(ret) << dendl; | |
256 | } | |
257 | return ret; | |
258 | } |