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