1 /*
2 Copyright 2016-2017 Skytechnology sp. z o.o.
3
4 This file is part of LizardFS.
5
6 LizardFS is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, version 3.
9
10 LizardFS is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with LizardFS. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "common/platform.h"
20
21 #include "master/snapshot_task.h"
22
23 #include "master/filesystem_checksum.h"
24 #include "master/filesystem_metadata.h"
25 #include "master/filesystem_operations.h"
26 #include "master/filesystem_quota.h"
27
cloneNodeTest(FSNode * src_node,FSNode * dst_node,FSNodeDirectory * dst_parent)28 int SnapshotTask::cloneNodeTest(FSNode *src_node, FSNode *dst_node, FSNodeDirectory *dst_parent) {
29 if (fsnodes_quota_exceeded_ug(src_node, {{QuotaResource::kInodes, 1}}) ||
30 fsnodes_quota_exceeded_dir(dst_parent, {{QuotaResource::kInodes, 1}})) {
31 return LIZARDFS_ERROR_QUOTA;
32 }
33 if (src_node->type == FSNode::kFile &&
34 (fsnodes_quota_exceeded_ug(src_node, {{QuotaResource::kSize, 1}}) ||
35 fsnodes_quota_exceeded_dir(dst_parent, {{QuotaResource::kSize, 1}}))) {
36 return LIZARDFS_ERROR_QUOTA;
37 }
38 if (dst_node) {
39 if (orig_inode_ != 0 && dst_node->id == orig_inode_) {
40 return LIZARDFS_ERROR_EINVAL;
41 }
42 if (dst_node->type != src_node->type) {
43 return LIZARDFS_ERROR_EPERM;
44 }
45 if (src_node->type != FSNode::kDirectory && !can_overwrite_) {
46 return LIZARDFS_ERROR_EEXIST;
47 }
48 }
49 return LIZARDFS_STATUS_OK;
50 }
51
cloneToExistingNode(uint32_t ts,FSNode * src_node,FSNodeDirectory * dst_parent,FSNode * dst_node)52 FSNode *SnapshotTask::cloneToExistingNode(uint32_t ts, FSNode *src_node,
53 FSNodeDirectory *dst_parent, FSNode *dst_node) {
54 assert(src_node->type == dst_node->type);
55
56 switch (src_node->type) {
57 case FSNode::kDirectory:
58 cloneDirectoryData(static_cast<const FSNodeDirectory *>(src_node),
59 static_cast<FSNodeDirectory *>(dst_node));
60 break;
61 case FSNode::kFile:
62 dst_node = cloneToExistingFileNode(ts, static_cast<FSNodeFile *>(src_node),
63 dst_parent, static_cast<FSNodeFile *>(dst_node));
64 break;
65 case FSNode::kSymlink:
66 cloneSymlinkData(static_cast<FSNodeSymlink *>(src_node),
67 static_cast<FSNodeSymlink *>(dst_node), dst_parent);
68 break;
69 case FSNode::kBlockDev:
70 case FSNode::kCharDev:
71 static_cast<FSNodeDevice *>(dst_node)->rdev =
72 static_cast<FSNodeDevice *>(src_node)->rdev;
73 }
74
75 dst_node->mode = src_node->mode;
76 dst_node->uid = src_node->uid;
77 dst_node->gid = src_node->gid;
78 dst_node->atime = src_node->atime;
79 dst_node->mtime = src_node->mtime;
80 dst_node->ctime = ts;
81
82 return dst_node;
83 }
84
cloneToNewNode(uint32_t ts,FSNode * src_node,FSNodeDirectory * dst_parent)85 FSNode *SnapshotTask::cloneToNewNode(uint32_t ts, FSNode *src_node, FSNodeDirectory *dst_parent) {
86 FSNode *dst_node = fsnodes_create_node(
87 ts, dst_parent, current_subtask_->second, src_node->type, src_node->mode, 0,
88 src_node->uid, src_node->gid, 0, AclInheritance::kDontInheritAcl, dst_inode_);
89
90 dst_node->goal = src_node->goal;
91 dst_node->trashtime = src_node->trashtime;
92 dst_node->mode = src_node->mode;
93 dst_node->atime = src_node->atime;
94 dst_node->mtime = src_node->mtime;
95
96 switch (src_node->type) {
97 case FSNode::kDirectory:
98 cloneDirectoryData(static_cast<const FSNodeDirectory *>(src_node),
99 static_cast<FSNodeDirectory *>(dst_node));
100 break;
101 case FSNode::kFile:
102 cloneChunkData(static_cast<FSNodeFile *>(src_node),
103 static_cast<FSNodeFile *>(dst_node), dst_parent);
104 break;
105 case FSNode::kSymlink:
106 cloneSymlinkData(static_cast<FSNodeSymlink *>(src_node),
107 static_cast<FSNodeSymlink *>(dst_node), dst_parent);
108 break;
109 case FSNode::kBlockDev:
110 case FSNode::kCharDev:
111 static_cast<FSNodeDevice *>(dst_node)->rdev =
112 static_cast<FSNodeDevice *>(src_node)->rdev;
113 }
114
115 return dst_node;
116 }
117
cloneToExistingFileNode(uint32_t ts,FSNodeFile * src_node,FSNodeDirectory * dst_parent,FSNodeFile * dst_node)118 FSNodeFile *SnapshotTask::cloneToExistingFileNode(uint32_t ts, FSNodeFile *src_node,
119 FSNodeDirectory *dst_parent, FSNodeFile *dst_node) {
120 bool same = dst_node->length == src_node->length && dst_node->chunks == src_node->chunks;
121
122 if (same) {
123 return dst_node;
124 }
125
126 fsnodes_unlink(ts, dst_parent, current_subtask_->second, dst_node);
127 dst_node = static_cast<FSNodeFile *>(fsnodes_create_node(
128 ts, dst_parent, current_subtask_->second, FSNode::kFile, src_node->mode, 0,
129 src_node->uid, src_node->gid, 0, AclInheritance::kDontInheritAcl, dst_inode_));
130
131 cloneChunkData(src_node, dst_node, dst_parent);
132
133 return dst_node;
134 }
135
cloneChunkData(const FSNodeFile * src_node,FSNodeFile * dst_node,FSNodeDirectory * dst_parent)136 void SnapshotTask::cloneChunkData(const FSNodeFile *src_node, FSNodeFile *dst_node,
137 FSNodeDirectory *dst_parent) {
138 statsrecord psr, nsr;
139
140 fsnodes_get_stats(dst_node, &psr);
141
142 dst_node->goal = src_node->goal;
143 dst_node->trashtime = src_node->trashtime;
144 dst_node->chunks = src_node->chunks;
145 dst_node->length = src_node->length;
146 for (uint32_t i = 0; i < src_node->chunks.size(); ++i) {
147 auto chunkid = src_node->chunks[i];
148 if (chunkid > 0) {
149 if (chunk_add_file(chunkid, dst_node->goal) != LIZARDFS_STATUS_OK) {
150 lzfs_pretty_syslog(LOG_ERR,
151 "structure error - chunk %016" PRIX64
152 " not found (inode: %" PRIu32 " ; index: %" PRIu32 ")",
153 chunkid, src_node->id, i);
154 }
155 }
156 }
157
158 fsnodes_get_stats(dst_node, &nsr);
159 fsnodes_add_sub_stats(dst_parent, &nsr, &psr);
160 fsnodes_quota_update(dst_node, {{QuotaResource::kSize, nsr.size - psr.size}});
161 }
162
cloneDirectoryData(const FSNodeDirectory * src_node,FSNodeDirectory * dst_node)163 void SnapshotTask::cloneDirectoryData(const FSNodeDirectory *src_node, FSNodeDirectory *dst_node) {
164 if (!enqueue_work_) {
165 return;
166 }
167 SubtaskContainer data;
168 data.reserve(src_node->entries.size());
169 for (const auto &entry : src_node->entries) {
170 auto local_id = entry.second->id;
171 data.emplace_back(std::move(local_id), (HString)entry.first);
172 }
173 if (!data.empty()) {
174 auto task = new SnapshotTask(std::move(data), orig_inode_,
175 dst_node->id, 0, can_overwrite_,
176 ignore_missing_src_,
177 emit_changelog_, enqueue_work_);
178 local_tasks_.push_back(*task);
179 }
180 }
181
cloneSymlinkData(FSNodeSymlink * src_node,FSNodeSymlink * dst_node,FSNodeDirectory * dst_parent)182 void SnapshotTask::cloneSymlinkData(FSNodeSymlink *src_node, FSNodeSymlink *dst_node,
183 FSNodeDirectory *dst_parent) {
184 statsrecord psr, nsr;
185
186 fsnodes_get_stats(dst_node, &psr);
187
188 dst_node->path = src_node->path;
189 dst_node->path_length = src_node->path_length;
190
191 fsnodes_get_stats(dst_node, &nsr);
192 fsnodes_add_sub_stats(dst_parent, &nsr, &psr);
193 }
194
emitChangelog(uint32_t ts,uint32_t dst_inode)195 void SnapshotTask::emitChangelog(uint32_t ts, uint32_t dst_inode) {
196 if (!emit_changelog_) {
197 gMetadata->metaversion++;
198 return;
199 }
200
201 fs_changelog(ts, "CLONE(%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%s,%" PRIu8 ")",
202 current_subtask_->first, dst_parent_inode_, dst_inode,
203 fsnodes_escape_name(current_subtask_->second).c_str(), can_overwrite_);
204 }
205
cloneNode(uint32_t ts)206 int SnapshotTask::cloneNode(uint32_t ts) {
207 FSNode *src_node = fsnodes_id_to_node(current_subtask_->first);
208 FSNodeDirectory *dst_parent = fsnodes_id_to_node<FSNodeDirectory>(dst_parent_inode_);
209
210 if (!src_node || src_node->type == FSNode::kTrash || src_node->type == FSNode::kReserved) {
211 return LIZARDFS_ERROR_ENOENT;
212 }
213 if (!dst_parent || dst_parent->type != FSNode::kDirectory) {
214 return LIZARDFS_ERROR_EINVAL;
215 }
216
217 FSNode *dst_node = fsnodes_lookup(dst_parent, current_subtask_->second);
218
219 int status = cloneNodeTest(src_node, dst_node, dst_parent);
220 if (status != LIZARDFS_STATUS_OK) {
221 return status;
222 }
223
224 if (dst_node) {
225 dst_node = cloneToExistingNode(ts, src_node, dst_parent, dst_node);
226 } else {
227 dst_node = cloneToNewNode(ts, src_node, dst_parent);
228 }
229
230 assert(dst_node);
231 fsnodes_update_checksum(dst_node);
232 fsnodes_update_checksum(dst_parent);
233 emitChangelog(ts, dst_node->id);
234 if (dst_inode_ != 0 && dst_inode_ != dst_node->id) {
235 return LIZARDFS_ERROR_MISMATCH;
236 }
237 return LIZARDFS_STATUS_OK;
238 }
239
execute(uint32_t ts,intrusive_list<Task> & work_queue)240 int SnapshotTask::execute(uint32_t ts, intrusive_list<Task> &work_queue) {
241 assert(current_subtask_ != subtask_.end());
242
243 int status = cloneNode(ts);
244 ++current_subtask_;
245
246 if (ignore_missing_src_ && status == LIZARDFS_ERROR_ENOENT) {
247 return LIZARDFS_STATUS_OK;
248 }
249 if (status == LIZARDFS_STATUS_OK) {
250 work_queue.splice(work_queue.end(), local_tasks_);
251 }
252
253 return status;
254 }
255