diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-14 00:51:05 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-14 00:51:05 +0000 |
commit | 829bbd3b5701f656c94f1cc672faa39946675d13 (patch) | |
tree | dbe6672228a3cb51eb8031ba714bb4abb96decf4 /loginutils | |
parent | f2b39e088d6ccbf4a540c741059c2f661eebc9ac (diff) | |
download | busybox-w32-829bbd3b5701f656c94f1cc672faa39946675d13.tar.gz busybox-w32-829bbd3b5701f656c94f1cc672faa39946675d13.tar.bz2 busybox-w32-829bbd3b5701f656c94f1cc672faa39946675d13.zip |
*: unify concurrent-safe update of /etc/{passwd,group,[g]shadow}
by Tito (farmatito AT tiscali.it)
function old new delta
update_passwd 743 1171 +428
bb_perror_nomsg - 9 +9
find_main 436 444 +8
passwd_main 1023 1027 +4
nameval 202 206 +4
chpasswd_main 315 319 +4
bb__parsespent 119 117 -2
adduser_main 654 650 -4
addgroup_main 345 341 -4
sv_main 1228 1222 -6
deluser_main 173 160 -13
bb_internal_putpwent 69 - -69
add_user_to_group 231 - -231
del_line_matching 460 31 -429
------------------------------------------------------------------------------
(add/remove: 1/2 grow/shrink: 5/6 up/down: 457/-758) Total: -301 bytes
Diffstat (limited to 'loginutils')
-rw-r--r-- | loginutils/addgroup.c | 75 | ||||
-rw-r--r-- | loginutils/adduser.c | 40 | ||||
-rw-r--r-- | loginutils/chpasswd.c | 5 | ||||
-rw-r--r-- | loginutils/deluser.c | 133 | ||||
-rw-r--r-- | loginutils/passwd.c | 5 |
5 files changed, 60 insertions, 198 deletions
diff --git a/loginutils/addgroup.c b/loginutils/addgroup.c index 5032d7b99..5a0cf3fff 100644 --- a/loginutils/addgroup.c +++ b/loginutils/addgroup.c | |||
@@ -9,7 +9,6 @@ | |||
9 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | 9 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
10 | * | 10 | * |
11 | */ | 11 | */ |
12 | |||
13 | #include "libbb.h" | 12 | #include "libbb.h" |
14 | 13 | ||
15 | static void xgroup_study(struct group *g) | 14 | static void xgroup_study(struct group *g) |
@@ -45,8 +44,8 @@ static void xgroup_study(struct group *g) | |||
45 | /* append a new user to the passwd file */ | 44 | /* append a new user to the passwd file */ |
46 | static void new_group(char *group, gid_t gid) | 45 | static void new_group(char *group, gid_t gid) |
47 | { | 46 | { |
48 | FILE *file; | ||
49 | struct group gr; | 47 | struct group gr; |
48 | char *p; | ||
50 | 49 | ||
51 | /* make sure gid and group haven't already been allocated */ | 50 | /* make sure gid and group haven't already been allocated */ |
52 | gr.gr_gid = gid; | 51 | gr.gr_gid = gid; |
@@ -54,67 +53,17 @@ static void new_group(char *group, gid_t gid) | |||
54 | xgroup_study(&gr); | 53 | xgroup_study(&gr); |
55 | 54 | ||
56 | /* add entry to group */ | 55 | /* add entry to group */ |
57 | file = xfopen(bb_path_group_file, "a"); | 56 | p = xasprintf("x:%u:", gr.gr_gid); |
58 | /* group:passwd:gid:userlist */ | 57 | if (update_passwd(bb_path_group_file, group, p, NULL) < 0) |
59 | fprintf(file, "%s:x:%u:\n", group, (unsigned)gr.gr_gid); | 58 | exit(EXIT_FAILURE); |
60 | if (ENABLE_FEATURE_CLEAN_UP) | 59 | if (ENABLE_FEATURE_CLEAN_UP) |
61 | fclose(file); | 60 | free(p); |
62 | #if ENABLE_FEATURE_SHADOWPASSWDS | 61 | #if ENABLE_FEATURE_SHADOWPASSWDS |
63 | file = fopen_or_warn(bb_path_gshadow_file, "a"); | 62 | /* Ignore errors: if file is missing we suppose admin doesn't want it */ |
64 | if (file) { | 63 | update_passwd(bb_path_gshadow_file, group, "!::", NULL); |
65 | fprintf(file, "%s:!::\n", group); | ||
66 | if (ENABLE_FEATURE_CLEAN_UP) | ||
67 | fclose(file); | ||
68 | } | ||
69 | #endif | 64 | #endif |
70 | } | 65 | } |
71 | 66 | ||
72 | #if ENABLE_FEATURE_ADDUSER_TO_GROUP | ||
73 | static void add_user_to_group(char **args, | ||
74 | const char *path, | ||
75 | FILE* FAST_FUNC (*fopen_func)(const char *fileName, const char *mode)) | ||
76 | { | ||
77 | char *line; | ||
78 | int len = strlen(args[1]); | ||
79 | llist_t *plist = NULL; | ||
80 | FILE *group_file; | ||
81 | |||
82 | group_file = fopen_func(path, "r"); | ||
83 | |||
84 | if (!group_file) return; | ||
85 | |||
86 | while ((line = xmalloc_fgetline(group_file)) != NULL) { | ||
87 | /* Find the group */ | ||
88 | if (!strncmp(line, args[1], len) | ||
89 | && line[len] == ':' | ||
90 | ) { | ||
91 | /* Add the new user */ | ||
92 | line = xasprintf("%s%s%s", line, | ||
93 | last_char_is(line, ':') ? "" : ",", | ||
94 | args[0]); | ||
95 | } | ||
96 | llist_add_to_end(&plist, line); | ||
97 | } | ||
98 | |||
99 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
100 | fclose(group_file); | ||
101 | group_file = fopen_func(path, "w"); | ||
102 | while ((line = llist_pop(&plist))) { | ||
103 | if (group_file) | ||
104 | fprintf(group_file, "%s\n", line); | ||
105 | free(line); | ||
106 | } | ||
107 | if (group_file) | ||
108 | fclose(group_file); | ||
109 | } else { | ||
110 | group_file = fopen_func(path, "w"); | ||
111 | if (group_file) | ||
112 | while ((line = llist_pop(&plist))) | ||
113 | fprintf(group_file, "%s\n", line); | ||
114 | } | ||
115 | } | ||
116 | #endif | ||
117 | |||
118 | /* | 67 | /* |
119 | * addgroup will take a login_name as its first parameter. | 68 | * addgroup will take a login_name as its first parameter. |
120 | * | 69 | * |
@@ -166,10 +115,12 @@ int addgroup_main(int argc UNUSED_PARAM, char **argv) | |||
166 | return EXIT_SUCCESS; | 115 | return EXIT_SUCCESS; |
167 | } | 116 | } |
168 | } | 117 | } |
169 | add_user_to_group(argv, bb_path_group_file, xfopen); | 118 | if (update_passwd(bb_path_group_file, argv[1], NULL, argv[0]) < 0) { |
170 | #if ENABLE_FEATURE_SHADOWPASSWDS | 119 | return EXIT_FAILURE; |
171 | add_user_to_group(argv, bb_path_gshadow_file, fopen_or_warn); | 120 | } |
172 | #endif | 121 | # if ENABLE_FEATURE_SHADOWPASSWDS |
122 | update_passwd(bb_path_gshadow_file, argv[1], NULL, argv[0]); | ||
123 | # endif | ||
173 | } else | 124 | } else |
174 | #endif /* ENABLE_FEATURE_ADDUSER_TO_GROUP */ | 125 | #endif /* ENABLE_FEATURE_ADDUSER_TO_GROUP */ |
175 | { | 126 | { |
diff --git a/loginutils/adduser.c b/loginutils/adduser.c index b94bb3aea..d0a870c54 100644 --- a/loginutils/adduser.c +++ b/loginutils/adduser.c | |||
@@ -7,14 +7,12 @@ | |||
7 | * | 7 | * |
8 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | 8 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
9 | */ | 9 | */ |
10 | |||
11 | #include "libbb.h" | 10 | #include "libbb.h" |
12 | 11 | ||
13 | #define OPT_DONT_SET_PASS (1 << 4) | 12 | #define OPT_DONT_SET_PASS (1 << 4) |
14 | #define OPT_SYSTEM_ACCOUNT (1 << 5) | 13 | #define OPT_SYSTEM_ACCOUNT (1 << 5) |
15 | #define OPT_DONT_MAKE_HOME (1 << 6) | 14 | #define OPT_DONT_MAKE_HOME (1 << 6) |
16 | 15 | ||
17 | |||
18 | /* remix */ | 16 | /* remix */ |
19 | /* recoded such that the uid may be passed in *p */ | 17 | /* recoded such that the uid may be passed in *p */ |
20 | static void passwd_study(struct passwd *p) | 18 | static void passwd_study(struct passwd *p) |
@@ -88,10 +86,7 @@ int adduser_main(int argc UNUSED_PARAM, char **argv) | |||
88 | { | 86 | { |
89 | struct passwd pw; | 87 | struct passwd pw; |
90 | const char *usegroup = NULL; | 88 | const char *usegroup = NULL; |
91 | FILE *file; | 89 | char *p; |
92 | #if ENABLE_FEATURE_SHADOWPASSWDS | ||
93 | int fd; | ||
94 | #endif | ||
95 | 90 | ||
96 | #if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS | 91 | #if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS |
97 | applet_long_options = adduser_longopts; | 92 | applet_long_options = adduser_longopts; |
@@ -124,32 +119,19 @@ int adduser_main(int argc UNUSED_PARAM, char **argv) | |||
124 | /* make sure everything is kosher and setup uid && maybe gid */ | 119 | /* make sure everything is kosher and setup uid && maybe gid */ |
125 | passwd_study(&pw); | 120 | passwd_study(&pw); |
126 | 121 | ||
127 | /* add to passwd */ | 122 | p = xasprintf("x:%u:%u:%s:%s:%s", pw.pw_uid, pw.pw_gid, pw.pw_gecos, pw.pw_dir, pw.pw_shell); |
128 | file = xfopen(bb_path_passwd_file, "a"); | 123 | if (update_passwd(bb_path_passwd_file, pw.pw_name, p, NULL) < 0) { |
129 | //fseek(file, 0, SEEK_END); /* paranoia, "a" should ensure that anyway */ | 124 | return EXIT_FAILURE; |
130 | if (putpwent(&pw, file) != 0) { | ||
131 | bb_perror_nomsg_and_die(); | ||
132 | } | 125 | } |
133 | /* do fclose even if !ENABLE_FEATURE_CLEAN_UP. | 126 | if (ENABLE_FEATURE_CLEAN_UP) |
134 | * We will exec passwd, files must be flushed & closed before that! */ | 127 | free(p); |
135 | fclose(file); | ||
136 | 128 | ||
137 | #if ENABLE_FEATURE_SHADOWPASSWDS | 129 | #if ENABLE_FEATURE_SHADOWPASSWDS |
138 | /* add to shadow if necessary */ | 130 | p = xasprintf("!:%u:0:99999:7:::", (unsigned)(time(NULL) / 86400)); /* sp->sp_lstchg */ |
139 | /* fopen(..., "a"); would create shadow file, which is wrong. | 131 | /* Ignore errors: if file is missing we suppose admin doesn't want it */ |
140 | * If shadow file doesn't exist, admin probably does not want it */ | 132 | update_passwd(bb_path_shadow_file, pw.pw_name, p, NULL); |
141 | fd = open_or_warn(bb_path_shadow_file, O_WRONLY | O_APPEND); | 133 | if (ENABLE_FEATURE_CLEAN_UP) |
142 | if (fd >= 0) { | 134 | free(p); |
143 | char *s = xasprintf("%s:!:%u:0:99999:7:::\n", | ||
144 | pw.pw_name, /* username */ | ||
145 | (unsigned)(time(NULL) / 86400) /* sp->sp_lstchg */ | ||
146 | /*0,*/ /* sp->sp_min */ | ||
147 | /*99999,*/ /* sp->sp_max */ | ||
148 | /*7*/ /* sp->sp_warn */ | ||
149 | ); | ||
150 | xwrite_str(fd, s); | ||
151 | close(fd); | ||
152 | } | ||
153 | #endif | 135 | #endif |
154 | 136 | ||
155 | /* add to group */ | 137 | /* add to group */ |
diff --git a/loginutils/chpasswd.c b/loginutils/chpasswd.c index c83d1dad7..4bffbe83f 100644 --- a/loginutils/chpasswd.c +++ b/loginutils/chpasswd.c | |||
@@ -5,7 +5,6 @@ | |||
5 | * Written for SLIND (from passwd.c) by Alexander Shishkin <virtuoso@slind.org> | 5 | * Written for SLIND (from passwd.c) by Alexander Shishkin <virtuoso@slind.org> |
6 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | 6 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
7 | */ | 7 | */ |
8 | |||
9 | #include "libbb.h" | 8 | #include "libbb.h" |
10 | 9 | ||
11 | #if ENABLE_GETOPT_LONG | 10 | #if ENABLE_GETOPT_LONG |
@@ -53,10 +52,10 @@ int chpasswd_main(int argc UNUSED_PARAM, char **argv) | |||
53 | /* This is rather complex: if user is not found in /etc/shadow, | 52 | /* This is rather complex: if user is not found in /etc/shadow, |
54 | * we try to find & change his passwd in /etc/passwd */ | 53 | * we try to find & change his passwd in /etc/passwd */ |
55 | #if ENABLE_FEATURE_SHADOWPASSWDS | 54 | #if ENABLE_FEATURE_SHADOWPASSWDS |
56 | rc = update_passwd(bb_path_shadow_file, name, pass); | 55 | rc = update_passwd(bb_path_shadow_file, name, pass, NULL); |
57 | if (rc == 0) /* no lines updated, no errors detected */ | 56 | if (rc == 0) /* no lines updated, no errors detected */ |
58 | #endif | 57 | #endif |
59 | rc = update_passwd(bb_path_passwd_file, name, pass); | 58 | rc = update_passwd(bb_path_passwd_file, name, pass, NULL); |
60 | /* LOGMODE_BOTH logs to syslog also */ | 59 | /* LOGMODE_BOTH logs to syslog also */ |
61 | logmode = LOGMODE_BOTH; | 60 | logmode = LOGMODE_BOTH; |
62 | if (rc < 0) | 61 | if (rc < 0) |
diff --git a/loginutils/deluser.c b/loginutils/deluser.c index 56253712e..293e324b0 100644 --- a/loginutils/deluser.c +++ b/loginutils/deluser.c | |||
@@ -9,117 +9,48 @@ | |||
9 | * Licensed under GPL version 2, see file LICENSE in this tarball for details. | 9 | * Licensed under GPL version 2, see file LICENSE in this tarball for details. |
10 | * | 10 | * |
11 | */ | 11 | */ |
12 | |||
13 | #include "libbb.h" | 12 | #include "libbb.h" |
14 | 13 | ||
15 | /* Status */ | 14 | static int del_line_matching(char **args, const char *filename) |
16 | #define STATUS_OK 0 | ||
17 | #define NAME_NOT_FOUND 1 | ||
18 | #define MEMBER_NOT_FOUND 2 | ||
19 | |||
20 | static void del_line_matching(char **args, | ||
21 | const char *filename, | ||
22 | FILE* FAST_FUNC (*fopen_func)(const char *fileName, const char *mode)) | ||
23 | { | 15 | { |
24 | FILE *passwd; | 16 | if (ENABLE_FEATURE_DEL_USER_FROM_GROUP && args[2]) { |
25 | smallint error = NAME_NOT_FOUND; | 17 | return update_passwd(filename, args[2], NULL, args[1]); |
26 | char *name = (ENABLE_FEATURE_DEL_USER_FROM_GROUP && args[2]) ? args[2] : args[1]; | ||
27 | char *line, *del; | ||
28 | char *new = xzalloc(1); | ||
29 | |||
30 | passwd = fopen_func(filename, "r"); | ||
31 | if (passwd) { | ||
32 | while ((line = xmalloc_fgets(passwd))) { | ||
33 | int len = strlen(name); | ||
34 | |||
35 | if (strncmp(line, name, len) == 0 | ||
36 | && line[len] == ':' | ||
37 | ) { | ||
38 | error = STATUS_OK; | ||
39 | if (ENABLE_FEATURE_DEL_USER_FROM_GROUP) { | ||
40 | struct group *gr; | ||
41 | char *p; | ||
42 | if (args[2] | ||
43 | /* There were two args on commandline */ | ||
44 | && (gr = getgrnam(name)) | ||
45 | /* The group was not deleted in the meanwhile */ | ||
46 | && (p = strrchr(line, ':')) | ||
47 | /* We can find a pointer to the last ':' */ | ||
48 | ) { | ||
49 | error = MEMBER_NOT_FOUND; | ||
50 | /* Move past ':' (worst case to '\0') and cut the line */ | ||
51 | p[1] = '\0'; | ||
52 | /* Reuse p */ | ||
53 | for (p = xzalloc(1); *gr->gr_mem != NULL; gr->gr_mem++) { | ||
54 | /* Add all the other group members */ | ||
55 | if (strcmp(args[1], *gr->gr_mem) != 0) { | ||
56 | del = p; | ||
57 | p = xasprintf("%s%s%s", p, p[0] ? "," : "", *gr->gr_mem); | ||
58 | free(del); | ||
59 | } else | ||
60 | error = STATUS_OK; | ||
61 | } | ||
62 | /* Recompose the line */ | ||
63 | line = xasprintf("%s%s\n", line, p); | ||
64 | if (ENABLE_FEATURE_CLEAN_UP) free(p); | ||
65 | } else | ||
66 | goto skip; | ||
67 | } | ||
68 | } | ||
69 | del = new; | ||
70 | new = xasprintf("%s%s", new, line); | ||
71 | free(del); | ||
72 | skip: | ||
73 | free(line); | ||
74 | } | ||
75 | |||
76 | if (ENABLE_FEATURE_CLEAN_UP) fclose(passwd); | ||
77 | |||
78 | if (error) { | ||
79 | if (ENABLE_FEATURE_DEL_USER_FROM_GROUP && error == MEMBER_NOT_FOUND) { | ||
80 | /* Set the correct values for error message */ | ||
81 | filename = name; | ||
82 | name = args[1]; | ||
83 | } | ||
84 | bb_error_msg("can't find %s in %s", name, filename); | ||
85 | } else { | ||
86 | passwd = fopen_func(filename, "w"); | ||
87 | if (passwd) { | ||
88 | fputs(new, passwd); | ||
89 | if (ENABLE_FEATURE_CLEAN_UP) fclose(passwd); | ||
90 | } | ||
91 | } | ||
92 | } | 18 | } |
93 | free(new); | 19 | return update_passwd(filename, args[1], NULL, NULL); |
94 | } | 20 | } |
95 | 21 | ||
96 | int deluser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 22 | int deluser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
97 | int deluser_main(int argc, char **argv) | 23 | int deluser_main(int argc, char **argv) |
98 | { | 24 | { |
99 | if (argc == 2 | 25 | if (argc != 2 |
100 | || (ENABLE_FEATURE_DEL_USER_FROM_GROUP | 26 | && (!ENABLE_FEATURE_DEL_USER_FROM_GROUP |
101 | && (applet_name[3] == 'g' && argc == 3)) | 27 | || (applet_name[3] != 'g' || argc != 3)) |
102 | ) { | 28 | ) { |
103 | if (geteuid()) | 29 | bb_show_usage(); |
104 | bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); | 30 | } |
105 | 31 | ||
106 | if ((ENABLE_FEATURE_DEL_USER_FROM_GROUP && argc != 3) | 32 | if (geteuid()) |
107 | || ENABLE_DELUSER | 33 | bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); |
108 | || (ENABLE_DELGROUP && ENABLE_DESKTOP) | 34 | |
35 | if ((ENABLE_FEATURE_DEL_USER_FROM_GROUP && argc != 3) | ||
36 | || ENABLE_DELUSER | ||
37 | || (ENABLE_DELGROUP && ENABLE_DESKTOP) | ||
38 | ) { | ||
39 | if (ENABLE_DELUSER | ||
40 | && (!ENABLE_DELGROUP || applet_name[3] == 'u') | ||
109 | ) { | 41 | ) { |
110 | if (ENABLE_DELUSER | 42 | if (del_line_matching(argv, bb_path_passwd_file) < 0) |
111 | && (!ENABLE_DELGROUP || applet_name[3] == 'u') | 43 | return EXIT_FAILURE; |
112 | ) { | 44 | if (ENABLE_FEATURE_SHADOWPASSWDS) { |
113 | del_line_matching(argv, bb_path_passwd_file, xfopen); | 45 | del_line_matching(argv, bb_path_shadow_file); |
114 | if (ENABLE_FEATURE_SHADOWPASSWDS) | 46 | } |
115 | del_line_matching(argv, bb_path_shadow_file, fopen_or_warn); | 47 | } else if (ENABLE_DESKTOP && ENABLE_DELGROUP && getpwnam(argv[1])) |
116 | } else if (ENABLE_DESKTOP && ENABLE_DELGROUP && getpwnam(argv[1])) | 48 | bb_error_msg_and_die("can't remove primary group of user %s", argv[1]); |
117 | bb_error_msg_and_die("can't remove primary group of user %s", argv[1]); | 49 | } |
118 | } | 50 | if (del_line_matching(argv, bb_path_group_file) < 0) |
119 | del_line_matching(argv, bb_path_group_file, xfopen); | 51 | return EXIT_FAILURE; |
120 | if (ENABLE_FEATURE_SHADOWPASSWDS) | 52 | if (ENABLE_FEATURE_SHADOWPASSWDS) { |
121 | del_line_matching(argv, bb_path_gshadow_file, fopen_or_warn); | 53 | del_line_matching(argv, bb_path_gshadow_file); |
122 | return EXIT_SUCCESS; | 54 | } |
123 | } else | 55 | return EXIT_SUCCESS; |
124 | bb_show_usage(); | ||
125 | } | 56 | } |
diff --git a/loginutils/passwd.c b/loginutils/passwd.c index 9ed78c1b7..7b93713b9 100644 --- a/loginutils/passwd.c +++ b/loginutils/passwd.c | |||
@@ -2,7 +2,6 @@ | |||
2 | /* | 2 | /* |
3 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | 3 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
4 | */ | 4 | */ |
5 | |||
6 | #include "libbb.h" | 5 | #include "libbb.h" |
7 | #include <syslog.h> | 6 | #include <syslog.h> |
8 | 7 | ||
@@ -181,12 +180,12 @@ int passwd_main(int argc UNUSED_PARAM, char **argv) | |||
181 | 180 | ||
182 | #if ENABLE_FEATURE_SHADOWPASSWDS | 181 | #if ENABLE_FEATURE_SHADOWPASSWDS |
183 | filename = bb_path_shadow_file; | 182 | filename = bb_path_shadow_file; |
184 | rc = update_passwd(bb_path_shadow_file, name, newp); | 183 | rc = update_passwd(bb_path_shadow_file, name, newp, NULL); |
185 | if (rc == 0) /* no lines updated, no errors detected */ | 184 | if (rc == 0) /* no lines updated, no errors detected */ |
186 | #endif | 185 | #endif |
187 | { | 186 | { |
188 | filename = bb_path_passwd_file; | 187 | filename = bb_path_passwd_file; |
189 | rc = update_passwd(bb_path_passwd_file, name, newp); | 188 | rc = update_passwd(bb_path_passwd_file, name, newp, NULL); |
190 | } | 189 | } |
191 | /* LOGMODE_BOTH */ | 190 | /* LOGMODE_BOTH */ |
192 | if (rc < 0) | 191 | if (rc < 0) |