netatalk  4.6.0
Free and Open Source Apple Filing Protocol (AFP) Server
Loading...
Searching...
No Matches
afppasswd_migrate.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2026 Daniel Markstedt <daniel@mindani.net>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#ifdef HAVE_CONFIG_H
16#include "config.h"
17#endif
18
19#include <dirent.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <inttypes.h>
23#include <pwd.h>
24#include <stdint.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <sys/param.h>
29#include <sys/stat.h>
30#include <sys/types.h>
31#include <unistd.h>
32
33#include <atalk/compat.h>
34#include <atalk/srp.h>
35
36#include "afppasswd_migrate.h"
37
38#define MIGRATE_ATTEMPTS 10000
39
41 char *line;
42 size_t length;
44 char *username;
45 uid_t uid;
46};
47
48static int lookup_uid(const char *name, uid_t *uid)
49{
50 const struct passwd *pwd;
51 errno = 0;
52 pwd = getpwnam(name);
53
54 if (pwd == NULL) {
55 return -1;
56 }
57
58 *uid = pwd->pw_uid;
59 return 0;
60}
61
62static int split_path(const char *path, char parent[MAXPATHLEN + 1],
63 char basename[MAXPATHLEN + 1])
64{
65 char copy[MAXPATHLEN + 1];
66 char *slash;
67 size_t length;
68
69 if (path == NULL || path[0] == '\0' ||
70 strlcpy(copy, path, sizeof(copy)) >= sizeof(copy)) {
71 fprintf(stderr, "afppasswd: migration path is empty or too long.\n");
72 return -1;
73 }
74
75 length = strlen(copy);
76
77 if (length > 1 && copy[length - 1] == '/') {
78 fprintf(stderr, "afppasswd: migration path must name a file.\n");
79 return -1;
80 }
81
82 slash = strrchr(copy, '/');
83
84 if (slash == NULL) {
85 strlcpy(parent, ".", MAXPATHLEN + 1);
86 strlcpy(basename, copy, MAXPATHLEN + 1);
87 } else if (slash == copy) {
88 strlcpy(parent, "/", MAXPATHLEN + 1);
89 strlcpy(basename, slash + 1, MAXPATHLEN + 1);
90 } else {
91 *slash = '\0';
92 strlcpy(parent, copy, MAXPATHLEN + 1);
93 strlcpy(basename, slash + 1, MAXPATHLEN + 1);
94 }
95
96 if (basename[0] == '\0' || strcmp(basename, ".") == 0 ||
97 strcmp(basename, "..") == 0) {
98 fprintf(stderr, "afppasswd: migration path must name a file.\n");
99 return -1;
100 }
101
102 return 0;
103}
104
105static int validate_parent(int fd, const char *path, uid_t administrator_uid)
106{
107 struct stat st;
108
109 if (fstat(fd, &st) < 0 || !S_ISDIR(st.st_mode) ||
110 st.st_uid != administrator_uid ||
111 (st.st_mode & (S_IWGRP | S_IWOTH))) {
112 fprintf(stderr,
113 "afppasswd: directory containing %s must be administrator-owned and not writable by group or other.\n",
114 path);
115 return -1;
116 }
117
118 return 0;
119}
120
121static int validate_source(int fd, const char *path, uid_t administrator_uid,
122 struct stat *st)
123{
124 if (fstat(fd, st) < 0) {
125 fprintf(stderr, "afppasswd: can't inspect legacy SRP file %s: %s\n",
126 path, strerror(errno));
127 return -1;
128 }
129
130 if (!S_ISREG(st->st_mode) || st->st_uid != administrator_uid ||
131 !srp_verifier_mode_is_safe(st->st_mode) || st->st_nlink != 1) {
132 fprintf(stderr,
133 "afppasswd: legacy SRP file %s must be a single-link regular file owned by the administrator and accessible only by its owner.\n",
134 path);
135 return -1;
136 }
137
138 return 0;
139}
140
141static void free_records(struct migrate_record *records, size_t count)
142{
143 for (size_t i = 0; i < count; i++) {
144 free(records[i].line);
145 free(records[i].username);
146 }
147
148 free(records);
149}
150
151static int add_record(struct migrate_record **records, size_t *count,
152 size_t *capacity, const char *line, size_t length,
153 size_t source_length, size_t line_number)
154{
155 struct migrate_record *grown;
156 struct migrate_record record = {0};
157 const char *colon = memchr(line, ':', length);
158 size_t username_length;
159
160 if (colon == NULL || memchr(line, '\0', length) != NULL) {
161 goto malformed;
162 }
163
164 username_length = (size_t)(colon - line);
165 record.username = strndup(line, username_length);
166
167 if (record.username == NULL) {
168 fprintf(stderr, "afppasswd: out of memory while migrating.\n");
169 return -1;
170 }
171
172 if (!srp_valid_username(record.username) ||
173 !srp_valid_fields(colon + 1)) {
174 goto malformed;
175 }
176
177 if (lookup_uid(record.username, &record.uid) < 0) {
178 fprintf(stderr,
179 "afppasswd: legacy SRP record %zu names unknown local user %s.\n",
180 line_number, record.username);
181 free(record.username);
182 return -1;
183 }
184
185 for (size_t i = 0; i < *count; i++) {
186 if (strcmp((*records)[i].username, record.username) == 0) {
187 fprintf(stderr,
188 "afppasswd: legacy SRP file contains duplicate user %s.\n",
189 record.username);
190 free(record.username);
191 return -1;
192 }
193
194 if ((*records)[i].uid == record.uid) {
195 fprintf(stderr,
196 "afppasswd: legacy SRP users %s and %s resolve to the same uid %ju.\n",
197 (*records)[i].username, record.username,
198 (uintmax_t)record.uid);
199 free(record.username);
200 return -1;
201 }
202 }
203
204 record.line = malloc(length + 1);
205
206 if (record.line == NULL) {
207 fprintf(stderr, "afppasswd: out of memory while migrating.\n");
208 free(record.username);
209 return -1;
210 }
211
212 memcpy(record.line, line, length);
213 record.line[length] = '\0';
214 record.length = length;
216
217 if (*count == *capacity) {
218 size_t new_capacity = *capacity == 0 ? 16 : *capacity * 2;
219
220 if (new_capacity < *capacity ||
221 new_capacity > SIZE_MAX / sizeof(**records)) {
222 fprintf(stderr, "afppasswd: too many legacy SRP records.\n");
223 free(record.line);
224 free(record.username);
225 return -1;
226 }
227
228 grown = realloc(*records, new_capacity * sizeof(**records));
229
230 if (grown == NULL) {
231 fprintf(stderr, "afppasswd: out of memory while migrating.\n");
232 free(record.line);
233 free(record.username);
234 return -1;
235 }
236
237 *records = grown;
238 *capacity = new_capacity;
239 }
240
241 (*records)[(*count)++] = record;
242 return 0;
243malformed:
244 fprintf(stderr, "afppasswd: malformed legacy SRP record %zu.\n",
245 line_number);
246 free(record.username);
247 return -1;
248}
249
250static int read_records(int source_fd, struct migrate_record **records,
251 size_t *count)
252{
253 FILE *stream;
254 char *line = NULL;
255 size_t size = 0, capacity = 0, line_number = 0;
256 ssize_t length;
257 int stream_fd = dup(source_fd);
258 int result = -1;
259
260 if (stream_fd < 0 || lseek(stream_fd, 0, SEEK_SET) < 0 ||
261 (stream = fdopen(stream_fd, "r")) == NULL) {
262 fprintf(stderr, "afppasswd: can't read legacy SRP file: %s\n",
263 strerror(errno));
264
265 if (stream_fd >= 0) {
266 close(stream_fd);
267 }
268
269 return -1;
270 }
271
272 while ((length = getline(&line, &size, stream)) >= 0) {
273 size_t source_length = (size_t)length;
274 line_number++;
275
277 fprintf(stderr, "afppasswd: malformed legacy SRP record %zu.\n",
278 line_number);
279 goto done;
280 }
281
282 if (source_length > 0 && line[source_length - 1] != '\n') {
283 char *grown = realloc(line, source_length + 2);
284
285 if (grown == NULL) {
286 fprintf(stderr, "afppasswd: out of memory while migrating.\n");
287 goto done;
288 }
289
290 line = grown;
291 size = source_length + 2;
292 line[source_length] = '\n';
293 length = (ssize_t)source_length + 1;
294 line[length] = '\0';
295 }
296
297 if (add_record(records, count, &capacity, line, (size_t)length,
298 source_length, line_number) < 0) {
299 goto done;
300 }
301 }
302
303 if (ferror(stream)) {
304 fprintf(stderr, "afppasswd: can't read legacy SRP file: %s\n",
305 strerror(errno));
306 goto done;
307 }
308
309 result = 0;
310done:
311 free(line);
312 fclose(stream);
313 return result;
314}
315
316static int source_unchanged(int source_fd,
317 const struct migrate_record *records,
318 size_t count)
319{
320 char buffer[4096];
321 size_t record = 0, offset = 0;
322 ssize_t length;
323
324 if (lseek(source_fd, 0, SEEK_SET) < 0) {
325 return 0;
326 }
327
328 while ((length = read(source_fd, buffer, sizeof(buffer))) > 0) {
329 for (ssize_t i = 0; i < length; i++) {
330 if (record >= count ||
331 buffer[i] != records[record].line[offset++]) {
332 return 0;
333 }
334
335 if (offset == records[record].source_length) {
336 record++;
337 offset = 0;
338 }
339 }
340 }
341
342 return length == 0 && record == count && offset == 0;
343}
344
345static int write_all(int fd, const char *data, size_t length)
346{
347 while (length > 0) {
348 ssize_t written = write(fd, data, length);
349
350 if (written < 0) {
351 return -1;
352 }
353
354 if (written == 0) {
355 errno = EIO;
356 return -1;
357 }
358
359 data += written;
360 length -= (size_t)written;
361 }
362
363 return 0;
364}
365
366static int uid_filename(uid_t uid, char *name, size_t size)
367{
368 int length = snprintf(name, size, "%ju", (uintmax_t)uid);
369 return length < 0 || (size_t)length >= size ? -1 : 0;
370}
371
372static int create_verifiers(int directory_fd, const char *path,
373 const struct migrate_record *records, size_t count,
374 uid_t administrator_uid)
375{
376 char name[3 * sizeof(uid_t) + 1];
377
378 for (size_t i = 0; i < count; i++) {
379 struct stat st;
380 int fd;
381 /* Validated disabled records retain administrator ownership. */
382 const char *fields = strchr(records[i].line, ':') + 1;
383 uid_t owner = *fields == '*' ? administrator_uid : records[i].uid;
384
385 if (uid_filename(records[i].uid, name, sizeof(name)) < 0) {
386 return -1;
387 }
388
389 fd = openat(directory_fd, name,
390 O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC | O_NOFOLLOW,
391 0600);
392
393 if (fd < 0) {
394 fprintf(stderr,
395 "afppasswd: can't create migrated verifier %s/%s: %s\n",
396 path, name, strerror(errno));
397 return -1;
398 }
399
400 if (fchmod(fd, 0600) < 0 ||
401 write_all(fd, records[i].line, records[i].length) < 0 ||
402 fchown(fd, owner, (gid_t) -1) < 0 ||
403 fsync(fd) < 0 ||
404 fstat(fd, &st) < 0) {
405 fprintf(stderr,
406 "afppasswd: can't create migrated verifier %s/%s: %s\n",
407 path, name, strerror(errno));
408 close(fd);
409 return -1;
410 }
411
412 if (!S_ISREG(st.st_mode) || st.st_uid != owner ||
413 !srp_verifier_mode_is_safe(st.st_mode) || st.st_nlink != 1) {
414 fprintf(stderr,
415 "afppasswd: migrated verifier %s/%s has unsafe metadata.\n",
416 path, name);
417 close(fd);
418 return -1;
419 }
420
421 if (close(fd) < 0) {
422 fprintf(stderr,
423 "afppasswd: can't close migrated verifier %s/%s: %s\n",
424 path, name, strerror(errno));
425 return -1;
426 }
427 }
428
429 return 0;
430}
431
432static void remove_staging(int parent_fd, const char *temporary_name,
433 const struct migrate_record *records, size_t count)
434{
435 char name[3 * sizeof(uid_t) + 1];
436 int directory_fd = openat(parent_fd, temporary_name,
437 O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
438
439 if (directory_fd < 0) {
440 return;
441 }
442
443 for (size_t i = 0; i < count; i++) {
444 if (uid_filename(records[i].uid, name, sizeof(name)) == 0) {
445 unlinkat(directory_fd, name, 0);
446 }
447 }
448
449 close(directory_fd);
450 unlinkat(parent_fd, temporary_name, AT_REMOVEDIR);
451}
452
453static int create_staging_directory(int parent_fd, const char *basename,
454 uid_t administrator_uid,
455 char temporary_name[MAXPATHLEN + 1])
456{
457 int fd;
458 int length = snprintf(temporary_name, MAXPATHLEN + 1,
459 ".%s.migrate.%ju", basename, (uintmax_t)getpid());
460
461 if (length < 0 || length > MAXPATHLEN) {
462 fprintf(stderr,
463 "afppasswd: migration path is too long for a temporary sibling.\n");
464 return -1;
465 }
466
467 if (mkdirat(parent_fd, temporary_name, 0700) < 0) {
468 fprintf(stderr,
469 "afppasswd: can't create migration directory: %s\n",
470 strerror(errno));
471 return -1;
472 }
473
474 fd = openat(parent_fd, temporary_name,
475 O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
476
477 if (fd < 0 || fchown(fd, administrator_uid, (gid_t) -1) < 0 ||
478 fchmod(fd, 0700) < 0) {
479 fprintf(stderr,
480 "afppasswd: can't prepare migration directory: %s\n",
481 strerror(errno));
482
483 if (fd >= 0) {
484 close(fd);
485 }
486
487 unlinkat(parent_fd, temporary_name, AT_REMOVEDIR);
488 return -1;
489 }
490
491 return fd;
492}
493
494static int source_still_at_path(int parent_fd, const char *basename,
495 const struct stat *source_st)
496{
497 struct stat st;
498 return fstatat(parent_fd, basename, &st, AT_SYMLINK_NOFOLLOW) == 0 &&
499 st.st_dev == source_st->st_dev && st.st_ino == source_st->st_ino &&
500 st.st_nlink == 1;
501}
502
503static int reject_stale_staging(int parent_fd, const char *basename,
504 const char *path)
505{
506 char prefix[MAXPATHLEN + 1];
507 DIR *directory;
508 struct dirent *entry;
509 int scan_fd, length;
510 size_t prefix_length;
511 length = snprintf(prefix, sizeof(prefix), ".%s.migrate.", basename);
512
513 if (length < 0 || (size_t)length >= sizeof(prefix)) {
514 fprintf(stderr,
515 "afppasswd: migration path is too long to inspect temporary siblings.\n");
516 return -1;
517 }
518
519 prefix_length = (size_t)length;
520 scan_fd = openat(parent_fd, ".",
521 O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
522
523 if (scan_fd < 0 || (directory = fdopendir(scan_fd)) == NULL) {
524 fprintf(stderr,
525 "afppasswd: can't inspect migration directory for %s: %s\n",
526 path, strerror(errno));
527
528 if (scan_fd >= 0) {
529 close(scan_fd);
530 }
531
532 return -1;
533 }
534
535 errno = 0;
536
537 while ((entry = readdir(directory)) != NULL) {
538 if (strncmp(entry->d_name, prefix, prefix_length) == 0) {
539 fprintf(stderr,
540 "afppasswd: found partial migration sibling %s; inspect or remove it before retrying.\n",
541 entry->d_name);
542 closedir(directory);
543 return -1;
544 }
545 }
546
547 if (errno != 0) {
548 fprintf(stderr,
549 "afppasswd: can't inspect migration directory for %s: %s\n",
550 path, strerror(errno));
551 closedir(directory);
552 return -1;
553 }
554
555 closedir(directory);
556 return 0;
557}
558
559static int link_backup(int parent_fd, const char *basename,
560 char backup_name[MAXPATHLEN + 1])
561{
562 for (unsigned int attempt = 0; attempt < MIGRATE_ATTEMPTS; attempt++) {
563 int length;
564
565 if (attempt == 0) {
566 length = snprintf(backup_name, MAXPATHLEN + 1, "%s.legacy",
567 basename);
568 } else {
569 length = snprintf(backup_name, MAXPATHLEN + 1, "%s.legacy.%u",
570 basename, attempt);
571 }
572
573 if (length < 0 || length > MAXPATHLEN) {
574 fprintf(stderr,
575 "afppasswd: migration path is too long for a backup sibling.\n");
576 return -1;
577 }
578
579 if (linkat(parent_fd, basename, parent_fd, backup_name, 0) == 0) {
580 return 0;
581 }
582
583 if (errno != EEXIST) {
584 fprintf(stderr, "afppasswd: can't retain legacy SRP file: %s\n",
585 strerror(errno));
586 return -1;
587 }
588 }
589
590 fprintf(stderr, "afppasswd: can't allocate a legacy backup name.\n");
591 return -1;
592}
593
599
600static enum restore_result restore_source(int parent_fd, const char *basename,
601 const char *backup_name)
602{
603 if (linkat(parent_fd, backup_name, parent_fd, basename, 0) < 0 ||
604 fsync(parent_fd) < 0) {
605 fprintf(stderr,
606 "afppasswd: automatic rollback failed; the original remains at %s.\n",
607 backup_name);
608 return RESTORE_FAILED;
609 }
610
611 if (unlinkat(parent_fd, backup_name, 0) < 0) {
612 fprintf(stderr,
613 "afppasswd: rollback restored the source but could not remove %s.\n",
614 backup_name);
616 }
617
618 if (fsync(parent_fd) < 0) {
619 fprintf(stderr,
620 "afppasswd: rollback restored the source but could not synchronize removal of %s.\n",
621 backup_name);
623 }
624
625 return RESTORE_COMPLETE;
626}
627
628int afppasswd_migrate_srp(const char *path, uid_t administrator_uid)
629{
630 struct migrate_record *records = NULL;
631 struct stat source_st;
632 struct flock lock = {0};
633 char parent[MAXPATHLEN + 1], basename[MAXPATHLEN + 1];
634 char temporary_name[MAXPATHLEN + 1] = {0};
635 char backup_name[MAXPATHLEN + 1] = {0};
636 int parent_fd = -1, source_fd = -1, temporary_fd = -1;
637 size_t count = 0;
638 int result = -1, source_unlinked = 0, directory_installed = 0;
639
640 if (split_path(path, parent, basename) < 0) {
641 goto done;
642 }
643
644 parent_fd = open(parent, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
645
646 if (parent_fd < 0 || validate_parent(parent_fd, path,
647 administrator_uid) < 0) {
648 if (parent_fd < 0) {
649 fprintf(stderr, "afppasswd: can't open directory for %s: %s\n",
650 path, strerror(errno));
651 }
652
653 goto done;
654 }
655
656 source_fd = openat(parent_fd, basename,
657 O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOFOLLOW);
658
659 if (source_fd < 0) {
660 struct stat st;
661
662 if (fstatat(parent_fd, basename, &st, AT_SYMLINK_NOFOLLOW) == 0 &&
663 S_ISDIR(st.st_mode)) {
664 fprintf(stderr,
665 "afppasswd: %s is already an SRP verifier directory; there is no legacy file to migrate.\n",
666 path);
667 } else {
668 fprintf(stderr, "afppasswd: can't open legacy SRP file %s: %s\n",
669 path, strerror(errno));
670 }
671
672 goto done;
673 }
674
675 if (fstat(source_fd, &source_st) == 0 && S_ISDIR(source_st.st_mode)) {
676 fprintf(stderr,
677 "afppasswd: %s is already an SRP verifier directory; there is no legacy file to migrate.\n",
678 path);
679 goto done;
680 }
681
682 if (validate_source(source_fd, path, administrator_uid, &source_st) < 0) {
683 goto done;
684 }
685
686 lock.l_type = F_RDLCK;
687 lock.l_whence = SEEK_SET;
688
689 if (fcntl(source_fd, F_SETLK, &lock) < 0) {
690 fprintf(stderr,
691 "afppasswd: legacy SRP file is busy; stop afpd and retry migration.\n");
692 goto done;
693 }
694
695 if (read_records(source_fd, &records, &count) < 0) {
696 goto done;
697 }
698
699 if (reject_stale_staging(parent_fd, basename, path) < 0) {
700 goto done;
701 }
702
703 temporary_fd = create_staging_directory(parent_fd, basename,
704 administrator_uid,
705 temporary_name);
706
707 if (temporary_fd < 0) {
708 /* Creation failed or the helper already cleaned up its directory. */
709 temporary_name[0] = '\0';
710 goto done;
711 }
712
713 if (create_verifiers(temporary_fd, path, records, count,
714 administrator_uid) < 0 ||
715 fchmod(temporary_fd, 0755) < 0 ||
716 fsync(temporary_fd) < 0) {
717 fprintf(stderr,
718 "afppasswd: migration staging failed; the legacy file was not changed.\n");
719 goto done;
720 }
721
722 if (!source_unchanged(source_fd, records, count) ||
723 !source_still_at_path(parent_fd, basename, &source_st)) {
724 fprintf(stderr,
725 "afppasswd: legacy SRP file changed during migration; stop afpd and retry.\n");
726 goto done;
727 }
728
729 if (link_backup(parent_fd, basename, backup_name) < 0) {
730 goto done;
731 }
732
733 if (fsync(parent_fd) < 0) {
734 fprintf(stderr,
735 "afppasswd: can't synchronize legacy SRP backup: %s\n",
736 strerror(errno));
737 unlinkat(parent_fd, backup_name, 0);
738 fsync(parent_fd);
739 backup_name[0] = '\0';
740 goto done;
741 }
742
743 if (unlinkat(parent_fd, basename, 0) < 0) {
744 fprintf(stderr, "afppasswd: can't prepare SRP directory install: %s\n",
745 strerror(errno));
746 unlinkat(parent_fd, backup_name, 0);
747 fsync(parent_fd);
748 backup_name[0] = '\0';
749 goto done;
750 }
751
752 source_unlinked = 1;
753
754 if (fsync(parent_fd) < 0 ||
755 renameat(parent_fd, temporary_name, parent_fd, basename) < 0) {
756 enum restore_result rollback_result;
757 fprintf(stderr,
758 "afppasswd: can't install SRP verifier directory; rolling back: %s\n",
759 strerror(errno));
760 rollback_result = restore_source(parent_fd, basename, backup_name);
761
762 if (rollback_result != RESTORE_FAILED) {
763 source_unlinked = 0;
764 }
765
766 if (rollback_result == RESTORE_COMPLETE) {
767 backup_name[0] = '\0';
768 }
769
770 goto done;
771 }
772
773 directory_installed = 1;
774 close(temporary_fd);
775 temporary_fd = -1;
776
777 if (fsync(parent_fd) < 0) {
778 fprintf(stderr,
779 "afppasswd: can't synchronize installed SRP directory; rolling back: %s\n",
780 strerror(errno));
781
782 if (renameat(parent_fd, basename, parent_fd, temporary_name) == 0) {
783 enum restore_result rollback_result;
784 directory_installed = 0;
785 rollback_result = restore_source(parent_fd, basename, backup_name);
786
787 if (rollback_result != RESTORE_FAILED) {
788 source_unlinked = 0;
789 }
790
791 if (rollback_result == RESTORE_COMPLETE) {
792 backup_name[0] = '\0';
793 }
794 } else {
795 fprintf(stderr,
796 "afppasswd: automatic rollback failed; the original remains at %s.\n",
797 backup_name);
798 }
799
800 goto done;
801 }
802
803 printf("afppasswd: migrated %zu SRP verifier%s; original retained as %s/%s\n",
804 count, count == 1 ? "" : "s", parent, backup_name);
805 result = 0;
806done:
807
808 if (temporary_fd >= 0) {
809 close(temporary_fd);
810 }
811
812 if (result < 0 && temporary_name[0] != '\0' && !directory_installed) {
813 remove_staging(parent_fd, temporary_name, records, count);
814 }
815
816 if (source_unlinked && !directory_installed) {
817 fprintf(stderr,
818 "afppasswd: recovery required; the original remains at %s/%s.\n",
819 parent, backup_name);
820 }
821
822 if (source_fd >= 0) {
823 close(source_fd);
824 }
825
826 if (parent_fd >= 0) {
827 close(parent_fd);
828 }
829
830 free_records(records, count);
831 return result;
832}
static int create_staging_directory(int parent_fd, const char *basename, uid_t administrator_uid, char temporary_name[MAXPATHLEN+1])
Definition afppasswd_migrate.c:453
static void free_records(struct migrate_record *records, size_t count)
Definition afppasswd_migrate.c:141
static int add_record(struct migrate_record **records, size_t *count, size_t *capacity, const char *line, size_t length, size_t source_length, size_t line_number)
Definition afppasswd_migrate.c:151
static void remove_staging(int parent_fd, const char *temporary_name, const struct migrate_record *records, size_t count)
Definition afppasswd_migrate.c:432
static int validate_source(int fd, const char *path, uid_t administrator_uid, struct stat *st)
Definition afppasswd_migrate.c:121
static int create_verifiers(int directory_fd, const char *path, const struct migrate_record *records, size_t count, uid_t administrator_uid)
Definition afppasswd_migrate.c:372
static int lookup_uid(const char *name, uid_t *uid)
Definition afppasswd_migrate.c:48
static int write_all(int fd, const char *data, size_t length)
Definition afppasswd_migrate.c:345
static int split_path(const char *path, char parent[MAXPATHLEN+1], char basename[MAXPATHLEN+1])
Definition afppasswd_migrate.c:62
static int link_backup(int parent_fd, const char *basename, char backup_name[MAXPATHLEN+1])
Definition afppasswd_migrate.c:559
static int reject_stale_staging(int parent_fd, const char *basename, const char *path)
Definition afppasswd_migrate.c:503
#define MIGRATE_ATTEMPTS
Definition afppasswd_migrate.c:38
static int uid_filename(uid_t uid, char *name, size_t size)
Definition afppasswd_migrate.c:366
static int validate_parent(int fd, const char *path, uid_t administrator_uid)
Definition afppasswd_migrate.c:105
static int read_records(int source_fd, struct migrate_record **records, size_t *count)
Definition afppasswd_migrate.c:250
restore_result
Definition afppasswd_migrate.c:594
@ RESTORE_FAILED
Definition afppasswd_migrate.c:595
@ RESTORE_COMPLETE
Definition afppasswd_migrate.c:597
@ RESTORE_CLEANUP_INCOMPLETE
Definition afppasswd_migrate.c:596
static int source_unchanged(int source_fd, const struct migrate_record *records, size_t count)
Definition afppasswd_migrate.c:316
static enum restore_result restore_source(int parent_fd, const char *basename, const char *backup_name)
Definition afppasswd_migrate.c:600
static int source_still_at_path(int parent_fd, const char *basename, const struct stat *source_st)
Definition afppasswd_migrate.c:494
int afppasswd_migrate_srp(const char *path, uid_t administrator_uid)
Definition afppasswd_migrate.c:628
size_t strlcpy(char *, const char *, size_t)
Definition strlcpy.c:36
#define data
Definition hash.c:52
#define S_ISDIR(s)
Definition megatron.h:96
char buffer[MAXPATHLEN+2]
Definition netacnv.c:35
#define SRP_USERNAME_MAX_LEN
Definition srp.h:14
#define SRP_FORMAT_LEN
Definition srp.h:23
Definition ad_open.c:96
Definition afppasswd_migrate.c:40
uid_t uid
Definition afppasswd_migrate.c:45
size_t source_length
Definition afppasswd_migrate.c:43
size_t length
Definition afppasswd_migrate.c:42
char * username
Definition afppasswd_migrate.c:44
char * line
Definition afppasswd_migrate.c:41
Definition include/atalk/directory.h:140
#define fchown
Definition test_afppasswd.c:45
#define fchmod
Definition test_afppasswd.c:46
#define fstat
Definition test_afppasswd.c:44
#define fsync
Definition test_afppasswd.c:48
#define fcntl
Definition test_afppasswd.c:47
#define unlinkat
Definition test_migrate.c:44
#define mkdirat
Definition test_migrate.c:43
#define write
Definition test_migrate.c:41
#define getpwnam
Definition test_migrate.c:40
#define NULL
Definition utf8util.c:47