storage: client_tipc: Implement file_move interface

Change-Id: Ic84426cd0c2bbc3dd1fcbe439a27c068b79068a8
diff --git a/client_tipc.c b/client_tipc.c
index 43e268b..1ad3570 100644
--- a/client_tipc.c
+++ b/client_tipc.c
@@ -294,6 +294,153 @@
 	return STORAGE_NO_ERROR;
 }
 
+/**
+ * storage_file_check_name - Check if file handle matches path
+ * @tr:         Transaction object.
+ * @file:       File handle object.
+ * @path:       Path to check.
+ *
+ * Return: %true if @file matches @path, %false otherwise.
+ */
+static bool storage_file_check_name(struct transaction *tr,
+                                    const struct file_handle *file,
+                                    const char *path)
+{
+	bool ret;
+	const struct file_info *file_info;
+	obj_ref_t ref = OBJ_REF_INITIAL_VALUE(ref);
+
+	file_info = file_get_info(tr, &file->block_mac, &ref);
+	if (!file_info) {
+		printf("can't read file entry at %lld\n",
+		       block_mac_to_block(tr, &file->block_mac));
+		return false;
+	}
+	assert(file_info);
+	ret = strcmp(file_info->path, path) == 0;
+	file_info_put(file_info, &ref);
+
+	return ret;
+}
+
+static enum storage_err storage_file_move(struct storage_msg *msg,
+                                          struct storage_file_move_req *req, size_t req_size,
+                                          struct storage_client_session *session)
+{
+	bool moved;
+	bool found;
+	enum storage_err result;
+	const char *old_name;
+	const char *new_name;
+	size_t fname_len;
+	size_t old_len;
+	size_t new_len;
+	uint32_t flags;
+	struct file_handle *file = NULL;
+	char path_buf[FS_PATH_MAX];
+	enum file_create_mode file_create_mode;
+	struct file_handle tmp_file;
+
+	if (req_size < sizeof(*req)) {
+		SS_ERR("%s: invalid request size (%zd)\n", __func__, req_size);
+		return STORAGE_ERR_NOT_VALID;
+	}
+
+	flags = req->flags;
+	if ((flags & ~STORAGE_FILE_MOVE_MASK) != 0) {
+		SS_ERR("invalid move flags 0x%x\n", flags);
+		return STORAGE_ERR_NOT_VALID;
+	}
+
+	if (flags & STORAGE_FILE_MOVE_CREATE) {
+		if (flags & STORAGE_FILE_MOVE_CREATE_EXCLUSIVE) {
+			file_create_mode = FILE_OPEN_CREATE_EXCLUSIVE;
+		} else {
+			file_create_mode = FILE_OPEN_CREATE;
+		}
+	} else {
+		file_create_mode = FILE_OPEN_NO_CREATE;
+	}
+
+	/* make sure filename is legal */
+	old_name = req->old_new_name;
+	fname_len = req_size - sizeof(*req);
+	if (!is_valid_name(old_name, fname_len)) {
+		SS_ERR("%s: invalid filename\n", __func__);
+		return STORAGE_ERR_NOT_VALID;
+	}
+
+	old_len = req->old_name_len;
+	if (old_len >= fname_len) {
+		SS_ERR("%s: invalid old filename length %u >= %u\n",
+		       __func__, old_len, fname_len);
+		return STORAGE_ERR_NOT_VALID;
+	}
+	new_len = fname_len - old_len;
+	new_name = old_name + old_len;
+
+	if (flags & STORAGE_FILE_MOVE_OPEN_FILE) {
+		file = get_file_handle(session, req->handle);
+		if (!file)
+			return STORAGE_ERR_NOT_VALID;
+	}
+
+	result = get_path(path_buf, sizeof(path_buf), &session->uuid,
+			  old_name, old_len);
+	if (result != STORAGE_NO_ERROR) {
+		return result;
+	}
+
+	SS_INFO("%s: old path %s\n", __func__, path_buf);
+
+	if (file) {
+		if (!storage_file_check_name(&session->tr, file, path_buf)) {
+			return STORAGE_ERR_NOT_VALID;
+		}
+	} else {
+		found = file_open(&session->tr, path_buf,
+				  &tmp_file, FILE_OPEN_NO_CREATE);
+		if (!found) {
+			return STORAGE_ERR_NOT_FOUND;
+		}
+		file = &tmp_file;
+	}
+
+	result = get_path(path_buf, sizeof(path_buf), &session->uuid,
+			  new_name, new_len);
+	if (result != STORAGE_NO_ERROR) {
+		if (file == &tmp_file) {
+			file_close(&tmp_file);
+		}
+		return result;
+	}
+	SS_INFO("%s: new path %s\n", __func__, path_buf);
+
+	moved = file_move(&session->tr, file, path_buf, file_create_mode);
+	if (file == &tmp_file) {
+		file_close(&tmp_file);
+	}
+
+	if (session->tr.failed) {
+		SS_ERR("%s: transaction failed\n", __func__);
+		return STORAGE_ERR_GENERIC;
+	} else if (!moved) {
+		return (flags & STORAGE_FILE_MOVE_CREATE) ?
+			STORAGE_ERR_EXIST : STORAGE_ERR_NOT_FOUND;
+	}
+
+	if (msg->flags & STORAGE_MSG_FLAG_TRANSACT_COMPLETE) {
+		transaction_complete(&session->tr);
+		if (session->tr.failed) {
+			SS_ERR("%s: transaction commit failed\n", __func__);
+			return STORAGE_ERR_GENERIC;
+		}
+		return STORAGE_NO_ERROR;
+	}
+
+	return STORAGE_NO_ERROR;
+}
+
 static int storage_file_open(struct storage_msg *msg,
                              struct storage_file_open_req *req, size_t req_size,
                              struct storage_client_session *session)
@@ -1033,6 +1180,9 @@
 	case STORAGE_FILE_DELETE:
 		result = storage_file_delete(msg, payload, payload_len, session);
 		break;
+	case STORAGE_FILE_MOVE:
+		result = storage_file_move(msg, payload, payload_len, session);
+		break;
 	case STORAGE_FILE_OPEN:
 		return storage_file_open(msg, payload, payload_len, session);
 	case STORAGE_FILE_CLOSE: