fuse_trash.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. #include <hdfs.h>
  19. #include <inttypes.h>
  20. #include <stdarg.h>
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <strings.h>
  25. #include "fuse_context_handle.h"
  26. #include "fuse_dfs.h"
  27. #include "fuse_trash.h"
  28. #include "fuse_users.h"
  29. #define TRASH_RENAME_TRIES 100
  30. #define ALREADY_IN_TRASH_ERR 9000
  31. /**
  32. * Split a path into a parent directory and a base path component.
  33. *
  34. * @param abs_path The absolute path.
  35. * @param pcomp (out param) Will be set to the last path component.
  36. * Malloced.
  37. * @param parent_dir (out param) Will be set to the parent directory.
  38. * Malloced.
  39. *
  40. * @return 0 on success.
  41. * On success, both *pcomp and *parent_dir will contain
  42. * malloc'ed strings.
  43. * EINVAL if the path wasn't absolute.
  44. * EINVAL if there is no parent directory (i.e. abs_path=/)
  45. * ENOMEM if we ran out of memory.
  46. */
  47. static int get_parent_dir(const char *abs_path, char **pcomp,
  48. char **parent_dir)
  49. {
  50. int ret;
  51. char *pdir = NULL, *pc = NULL, *last_slash;
  52. pdir = strdup(abs_path);
  53. if (!pdir) {
  54. ret = ENOMEM;
  55. goto done;
  56. }
  57. last_slash = rindex(pdir, '/');
  58. if (!last_slash) {
  59. ERROR("get_parent_dir(%s): expected absolute path.\n", abs_path);
  60. ret = EINVAL;
  61. goto done;
  62. }
  63. if (last_slash[1] == '\0') {
  64. *last_slash = '\0';
  65. last_slash = rindex(pdir, '/');
  66. if (!last_slash) {
  67. ERROR("get_parent_dir(%s): there is no parent dir.\n", abs_path);
  68. ret = EINVAL;
  69. goto done;
  70. }
  71. }
  72. pc = strdup(last_slash + 1);
  73. if (!pc) {
  74. ret = ENOMEM;
  75. goto done;
  76. }
  77. *last_slash = '\0';
  78. ret = 0;
  79. done:
  80. if (ret) {
  81. free(pdir);
  82. free(pc);
  83. return ret;
  84. }
  85. *pcomp = pc;
  86. *parent_dir = pdir;
  87. return 0;
  88. }
  89. /**
  90. * Get the base path to the trash. This will depend on the user ID.
  91. * For example, a user whose ID maps to 'foo' will get back the path
  92. * "/user/foo/.Trash/Current".
  93. *
  94. * @param trash_base (out param) the base path to the trash.
  95. * Malloced.
  96. *
  97. * @return 0 on success; error code otherwise.
  98. */
  99. static int get_trash_base(char **trash_base)
  100. {
  101. const char * const PREFIX = "/user/";
  102. const char * const SUFFIX = "/.Trash/Current";
  103. char *user_name = NULL, *base = NULL;
  104. uid_t uid = fuse_get_context()->uid;
  105. int ret;
  106. user_name = getUsername(uid);
  107. if (!user_name) {
  108. ERROR("get_trash_base(): failed to get username for uid %"PRId64"\n",
  109. (uint64_t)uid);
  110. ret = EIO;
  111. goto done;
  112. }
  113. if (asprintf(&base, "%s%s%s", PREFIX, user_name, SUFFIX) < 0) {
  114. base = NULL;
  115. ret = ENOMEM;
  116. goto done;
  117. }
  118. ret = 0;
  119. done:
  120. free(user_name);
  121. if (ret) {
  122. free(base);
  123. return ret;
  124. }
  125. *trash_base = base;
  126. return 0;
  127. }
  128. //
  129. // NOTE: this function is a c implementation of org.apache.hadoop.fs.Trash.moveToTrash(Path path).
  130. //
  131. int move_to_trash(const char *abs_path, hdfsFS userFS)
  132. {
  133. int ret;
  134. char *pcomp = NULL, *parent_dir = NULL, *trash_base = NULL;
  135. char *target_dir = NULL, *target = NULL;
  136. ret = get_parent_dir(abs_path, &pcomp, &parent_dir);
  137. if (ret) {
  138. goto done;
  139. }
  140. ret = get_trash_base(&trash_base);
  141. if (ret) {
  142. goto done;
  143. }
  144. int trash_base_len = strlen(trash_base);
  145. if (!strncmp(trash_base, abs_path, trash_base_len)
  146. && (strlen(abs_path) == trash_base_len || abs_path[trash_base_len] == '/')) {
  147. INFO("move_to_trash(%s): file is already in the trash; deleting.",
  148. abs_path);
  149. ret = ALREADY_IN_TRASH_ERR;
  150. goto done;
  151. }
  152. if (asprintf(&target_dir, "%s%s", trash_base, parent_dir) < 0) {
  153. ret = ENOMEM;
  154. target_dir = NULL;
  155. goto done;
  156. }
  157. if (asprintf(&target, "%s/%s", target_dir, pcomp) < 0) {
  158. ret = ENOMEM;
  159. target = NULL;
  160. goto done;
  161. }
  162. // create the target trash directory in trash (if needed)
  163. if (hdfsExists(userFS, target_dir) != 0) {
  164. // make the directory to put it in in the Trash - NOTE
  165. // hdfsCreateDirectory also creates parents, so Current will be created if it does not exist.
  166. if (hdfsCreateDirectory(userFS, target_dir)) {
  167. ret = errno;
  168. ERROR("move_to_trash(%s) error: hdfsCreateDirectory(%s) failed with error %d",
  169. abs_path, target_dir, ret);
  170. goto done;
  171. }
  172. } else if (hdfsExists(userFS, target) == 0) {
  173. // If there is already a file in the trash with this path, append a number.
  174. int idx;
  175. for (idx = 1; idx < TRASH_RENAME_TRIES; idx++) {
  176. free(target);
  177. if (asprintf(&target, "%s/%s.%d", target_dir, pcomp, idx) < 0) {
  178. target = NULL;
  179. ret = ENOMEM;
  180. goto done;
  181. }
  182. if (hdfsExists(userFS, target) != 0) {
  183. break;
  184. }
  185. }
  186. if (idx == TRASH_RENAME_TRIES) {
  187. ERROR("move_to_trash(%s) error: there are already %d files in the trash "
  188. "with this name.\n", abs_path, TRASH_RENAME_TRIES);
  189. ret = EINVAL;
  190. goto done;
  191. }
  192. }
  193. if (hdfsRename(userFS, abs_path, target)) {
  194. ret = errno;
  195. ERROR("move_to_trash(%s): failed to rename the file to %s: error %d",
  196. abs_path, target, ret);
  197. goto done;
  198. }
  199. ret = 0;
  200. done:
  201. if ((ret != 0) && (ret != ALREADY_IN_TRASH_ERR)) {
  202. ERROR("move_to_trash(%s) failed with error %d", abs_path, ret);
  203. }
  204. free(pcomp);
  205. free(parent_dir);
  206. free(trash_base);
  207. free(target_dir);
  208. free(target);
  209. return ret;
  210. }
  211. int hdfsDeleteWithTrash(hdfsFS userFS, const char *path, int useTrash)
  212. {
  213. int tried_to_move_to_trash = 0;
  214. if (useTrash) {
  215. tried_to_move_to_trash = 1;
  216. if (move_to_trash(path, userFS) == 0) {
  217. return 0;
  218. }
  219. }
  220. if (hdfsDelete(userFS, path, 1)) {
  221. int err = errno;
  222. if (err < 0) {
  223. err = -err;
  224. }
  225. ERROR("hdfsDeleteWithTrash(%s): hdfsDelete failed: error %d.",
  226. path, err);
  227. return -err;
  228. }
  229. if (tried_to_move_to_trash) {
  230. ERROR("hdfsDeleteWithTrash(%s): deleted the file instead.\n", path);
  231. }
  232. return 0;
  233. }