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