💾 Archived View for gemini.rmf-dev.com › repo › Vaati › mz › files › 762de32970ed22078aaef00f843b65d… captured on 2023-12-28 at 15:51:58. Gemini links have been rewritten to link to archived content

View Raw

More Information

-=-=-=-=-=-=-

Go Back

0 /*

1 * Copyright (c) 2023 RMF <rawmonk@firemail.cc>

2 *

3 * Permission to use, copy, modify, and distribute this software for any

4 * purpose with or without fee is hereby granted, provided that the above

5 * copyright notice and this permission notice appear in all copies.

6 *

7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES

8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF

9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR

10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES

11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN

12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF

13 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

14 */

15 #ifdef __linux__

16 #define _GNU_SOURCE

17 #else

18 #define _BSD_SOURCE

19 #endif

20 #include <stdio.h>

21 #include <stdlib.h>

22 #include <dirent.h>

23 #include <fcntl.h>

24 #include <errno.h>

25 #include <string.h>

26 #include <pwd.h>

27 #include <unistd.h>

28 #include <sys/stat.h>

29 #include <sys/types.h>

30 #include <fts.h>

31 #include "client.h"

32 #include "strlcpy.h"

33 #include "file.h"

34 #include "view.h"

35 #include "trash.h"

36 #include "util.h"

37

38 #define TRASH "/.trash"

39 #define ID_LENGTH 32

40

41 static int gethome(char *buf, size_t length) {

42

43 struct passwd *pw;

44 char *home;

45 int fd;

46

47 home = getenv("HOME");

48 if (home) {

49 fd = open(home, O_DIRECTORY);

50 if (fd > -1) {

51 close(fd);

52 return strlcpy(buf, home, length);

53 }

54 }

55

56 pw = getpwuid(geteuid());

57 if (!pw) return -1;

58 fd = open(pw->pw_dir, O_DIRECTORY);

59 if (fd < 0) return -1;

60 close(fd);

61 return strlcpy(buf, pw->pw_dir, length);

62 }

63

64 static int trash_path(char *path, size_t length) {

65

66 char buf[1024];

67

68 int len = gethome(buf, length);

69 if (len == -1 || len + sizeof(TRASH) >= length) goto clean;

70

71 len = strlcpy(path, buf, length);

72 strlcpy(&path[len], TRASH, sizeof(path) - length);

73

74 if (!strncmp(path, buf, length)) goto clean;

75

76 return 0;

77 clean: /* make sure we're not deleting a whole folder by accident */

78 memset(path, 0, length);

79 strlcpy(path, "/nonexistent/folder", sizeof(path));

80 return -1;

81 }

82

83 int trash_init() {

84 char path[PATH_MAX];

85 int home, trash;

86

87 if (gethome(V(path)) == -1) return -1;

88

89 home = open(path, O_DIRECTORY);

90 if (home < 0) goto fail;

91

92 trash = openat(home, ".trash", O_DIRECTORY);

93 if (trash > -1) {

94 close(home);

95 return trash;

96 }

97

98 if (mkdirat(home, ".trash", 0700)) goto fail;

99

100 trash = openat(home, ".trash", O_DIRECTORY);

101 if (trash < 0) goto fail;

102

103 close(home);

104 return trash;

105 fail:

106 if (home > -1)

107 close(home);

108 return -1;

109 }

110

111 int trash_send(int fd, char *path, char *name) {

112

113 char buf[PATH_MAX * 2], id[ID_LENGTH + 1];

114 int info, len, error;

115

116 info = openat(client.trash, "info", O_WRONLY|O_CREAT|O_APPEND, 0600);

117 if (info < 0)

118 return -1;

119

120 do {

121 size_t i;

122 int try;

123

124 i = 0;

125 while (i < sizeof(id) - 1) {

126 id[i] = 'a' + rand() % 26;

127 i++;

128 }

129 id[sizeof(id) - 1] = '\0';

130

131 /* check if there's not already a file with that id */

132 try = openat(client.trash, id, O_RDONLY);

133 if (try < 0) break;

134 close(i);

135 } while (1);

136

137 trash_path(V(buf));

138 error = file_move(path, fd, name, client.trash, buf, id);

139 if (!error) {

140 len = snprintf(V(buf), "%s %s/%s\n", id, path, name);

141 error = -(write(info, buf, len) != len);

142 }

143 close(info);

144

145 return error;

146 }

147

148 int trash_clear() {

149

150 char path[PATH_MAX], cmd[PATH_MAX * 2];

151

152 if (trash_path(V(path))) return -1;

153 snprintf(V(cmd), "rm -r %s", path);

154 if (system(cmd)) return -1;

155

156 close(client.trash);

157 client.trash = trash_init();

158 if (client.trash < 0) return -1;

159

160 return 0;

161 }

162

163 int trash_rawpath(struct view *view, char *out, size_t length) {

164

165 char path[PATH_MAX];

166

167 if (view->fd != TRASH_FD) return -1;

168 if (trash_path(V(path))) return -1;

169 if (snprintf(out, length, "%s/%s", path,

170 (char*)SELECTED(view).other) >= (int)length)

171 return -1;

172 return 0;

173 }

174

175 int trash_restore(struct view *view) {

176

177 size_t i;

178 char path[PATH_MAX];

179 int error = 0;

180

181 if (view->fd != TRASH_FD) {

182 errno = EINVAL;

183 return -1;

184 }

185

186 if (trash_path(V(path))) return -1;

187

188 i = 0;

189 while (i < view->length) {

190

191 char src[PATH_MAX];

192 char *id;

193 size_t j = i++;

194 int fd;

195

196 if (!view->entries[j].selected) continue;

197 id = view->entries[j].other;

198 snprintf(V(src), "%s/%s", path, id);

199

200 /* check if file exist before using rename */

201 fd = open(view->entries[j].name, 0);

202 if (fd > -1) {

203 close(fd);

204 errno = EEXIST;

205 error = -1;

206 continue;

207 }

208

209 if (rename(src, view->entries[j].name)) {

210 char *name;

211 int ret;

212 if (errno != EXDEV) return -1;

213 STRCPY(src, view->entries[j].name);

214 name = strrchr(src, '/');

215 if (!name) return -1;

216 *name = '\0';

217 name++;

218 fd = open(src, O_DIRECTORY);

219 if (fd < 0) return -1;

220 ret = file_move(path, client.trash, id, fd, src, name);

221 close(fd);

222 if (ret < 0) return -1;

223 }

224 view->entries[j].selected = -1;

225 }

226 return error;

227 }

228

229 int trash_refresh(struct view *view) {

230

231 void *next, *prev;

232 size_t i;

233 int fd, rewrite;

234

235 i = 0;

236 rewrite = 0;

237 while (i < view->length) {

238 switch (view->entries[i].selected) {

239 case -1:

240 rewrite = 1;

241 break;

242 case 1:

243 view->entries[i].selected = 0;

244 break;

245 }

246 i++;

247 }

248 if (!rewrite) return 0;

249

250 /* rewrite info file */

251 fd = openat(client.trash, "info", O_CREAT|O_WRONLY|O_TRUNC);

252 if (!fd) return -1;

253

254 i = 0;

255 while (i < view->length) {

256

257 char c;

258 size_t j = i++;

259

260 if (view->entries[j].selected == -1) continue;

261

262 write(fd, view->entries[j].other, ID_LENGTH);

263 c = ' ';

264 write(fd, &c, 1);

265 write(fd, view->entries[j].name,

266 strnlen(V(view->entries[j].name)));

267 c = '\n';

268 write(fd, &c, 1);

269 }

270 close(fd);

271

272 i = 0;

273 while (i < view->length) {

274 free(view->entries[i].other);

275 }

276

277 free(view->entries);

278

279 next = view->next;

280 prev = view->prev;

281 trash_view(view);

282 view->next = next;

283 view->prev = prev;

284

285 return 0;

286 }

287

288 int trash_view(struct view* view) {

289

290 int fd;

291 size_t i;

292 char buf[PATH_MAX * 2];

293

294 PZERO(view);

295 STRCPY(view->path, "Trash");

296 view->fd = TRASH_FD;

297

298 fd = openat(client.trash, "info", O_RDONLY);

299 if (fd < 0) return 0;

300

301 i = 0;

302 while (1) {

303

304 void *ptr;

305 char id[ID_LENGTH];

306 char c;

307 size_t j;

308

309 j = read(fd, V(id));

310 if (!j) { /* success : end of file */

311 close(fd);

312 qsort(view->entries, view->length,

313 sizeof(struct entry), file_sort);

314 return 0;

315 }

316 if (j != sizeof(id)) break;

317

318 j = 0;

319 while (j < sizeof(id)) {

320 if (id[j] > 'z' || id[j] < 'a') break;

321 j++;

322 }

323 if (j != sizeof(id)) break;

324

325 if (read(fd, &c, 1) != 1 || c != ' ') break;

326

327 j = 0;

328 while (j < sizeof(buf)) {

329 if (read(fd, &c, 1) != 1) break;

330 if (c == '\n') {

331 buf[j] = 0;

332 break;

333 }

334 buf[j] = c;

335 j++;

336 }

337

338 ptr = realloc(view->entries, sizeof(struct entry) * (i + 1));

339 if (!ptr) break;

340 view->entries = ptr;

341 RZERO(view->entries[i]);

342

343 STRCPY(view->entries[i].name, buf);

344 view->entries[i].type = DT_REG;

345 {

346 struct stat s;

347 char path[ID_LENGTH + 1];

348 memcpy(path, id, ID_LENGTH);

349 path[ID_LENGTH] = 0;

350 view->entries[i].type =

351 fstatat(client.trash, path, &s, 0) ?

352 DT_REG : (S_ISDIR(s.st_mode) ?

353 DT_DIR : DT_REG);

354 }

355 view->length = i + 1;

356

357 view->entries[i].other = calloc(ID_LENGTH + 1, 1);

358 if (!view->entries[i].other) break;

359 memcpy(view->entries[i].other, V(id));

360

361 i++;

362 }

363

364 for (i = 0; i < view->length; i++)

365 free(view->entries[i].other);

366 free(view->entries);

367 close(fd);

368 return -1;

369 }

370