2 Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
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.
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.
17 * Netatalk utility functions
22 #endif /* HAVE_CONFIG_H */
29 #include <sys/types.h>
38 #include <atalk/adouble.h>
40 #include <atalk/afp.h>
41 #include <atalk/logger.h>
42 #include <atalk/vfs.h>
43 #include <atalk/util.h>
44 #include <atalk/unix.h>
45 #include <atalk/compat.h>
46 #include <atalk/errchk.h>
47 #include <atalk/acl.h>
49 /* close all FDs >= a specified value */
50 static void closeall(int fd)
52 int fdlimit = sysconf(_SC_OPEN_MAX);
59 * Run command in a child and wait for it to finish
61 int run_cmd(const char *cmd, char **cmd_argv)
65 sigset_t sigs, oldsigs;
69 pthread_sigmask(SIG_SETMASK, &sigs, &oldsigs);
71 if ((pid = fork()) < 0) {
72 LOG(log_error, logtype_default, "run_cmd: fork: %s", strerror(errno));
79 execvp("mv", cmd_argv);
83 while ((wpid = waitpid(pid, &status, 0)) < 0) {
89 LOG(log_error, logtype_default, "waitpid(%d): %s", (int)pid, strerror(errno));
93 if (WIFEXITED(status))
94 status = WEXITSTATUS(status);
95 else if (WIFSIGNALED(status))
96 status = WTERMSIG(status);
98 LOG(log_note, logtype_default, "run_cmd(\"%s\"): status: %d", cmd, status);
103 pthread_sigmask(SIG_SETMASK, &oldsigs, NULL);
110 * Fork, exit parent, setsid(), optionally chdir("/"), optionally close all fds
112 * returns -1 on failure, but you can't do much except exit in that case
113 * since we may already have forked
115 int daemonize(int nochdir, int noclose)
143 open("/dev/null",O_RDWR);
151 static uid_t saved_uid = -1;
154 * seteuid(0) and back, if either fails and panic != 0 we PANIC
156 void become_root(void)
159 saved_uid = geteuid();
161 AFP_PANIC("Can't seteuid(0)");
165 void unbecome_root(void)
168 if (saved_uid == -1 || seteuid(saved_uid) < 0)
169 AFP_PANIC("Can't seteuid back");
175 * @brief get cwd in static buffer
177 * @returns pointer to path or pointer to error messages on error
179 const char *getcwdpath(void)
181 static char cwd[MAXPATHLEN + 1];
184 if ((p = getcwd(cwd, MAXPATHLEN)) != NULL)
187 return strerror(errno);
191 * @brief Request absolute path
193 * @returns Absolute filesystem path to object
195 const char *fullpathname(const char *name)
197 static char wd[MAXPATHLEN + 1];
202 if (getcwd(wd , MAXPATHLEN)) {
203 strlcat(wd, "/", MAXPATHLEN);
204 strlcat(wd, name, MAXPATHLEN);
206 strlcpy(wd, name, MAXPATHLEN);
213 * Takes a buffer with a path, strips slashs, returns basename
217 * "[/][dir/[...]]file"
219 * "[/][dir/[...]]dir/[/]"
220 * Result is "file" or "dir"
222 * @returns pointer to basename in path buffer, buffer is possibly modified
224 char *stripped_slashes_basename(char *p)
226 int i = strlen(p) - 1;
227 while (i > 0 && p[i] == '/')
229 return (strrchr(p, '/') ? strrchr(p, '/') + 1 : p);
232 /*********************************************************************************
233 * chdir(), chmod(), chown(), stat() wrappers taking an additional option.
234 * Currently the only used options are O_NOFOLLOW, used to switch between symlink
235 * behaviour, and O_NETATALK_ACL for ochmod() indicating chmod_acl() shall be
236 * called which does special ACL handling depending on the filesytem
237 *********************************************************************************/
239 int ostat(const char *path, struct stat *buf, int options)
241 if (options & O_NOFOLLOW)
242 return lstat(path, buf);
244 return stat(path, buf);
247 int ochown(const char *path, uid_t owner, gid_t group, int options)
249 if (options & O_NOFOLLOW)
250 return lchown(path, owner, group);
252 return chown(path, owner, group);
256 * chmod() wrapper for symlink and ACL handling
258 * @param path (r) path
259 * @param mode (r) requested mode
260 * @param sb (r) stat() of path or NULL
261 * @param option (r) O_NOFOLLOW | O_NETATALK_ACL
263 * Options description:
264 * O_NOFOLLOW: don't chmod() symlinks, do nothing, return 0
265 * O_NETATALK_ACL: call chmod_acl() instead of chmod()
266 * O_IGNORE: ignore chmod() request, directly return 0
268 int ochmod(char *path, mode_t mode, const struct stat *st, int options)
272 if (options & O_IGNORE)
276 if (lstat(path, &sb) != 0)
281 if (options & O_NOFOLLOW)
282 if (S_ISLNK(st->st_mode))
285 if (options & O_NETATALK_ACL) {
286 return chmod_acl(path, mode);
288 return chmod(path, mode);
293 * @brief ostat/fsstatat multiplexer
295 * ostatat mulitplexes ostat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored.
297 * @param dirfd (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD
298 * @param path (r) pathname
299 * @param st (rw) pointer to struct stat
301 int ostatat(int dirfd, const char *path, struct stat *st, int options)
306 return fstatat(dirfd, path, st, (options & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0);
308 return ostat(path, st, options);
316 * @brief symlink safe chdir replacement
318 * Only chdirs to dir if it doesn't contain symlinks or if symlink checking
321 * @returns 1 if a path element is a symlink, 0 otherwise, -1 on syserror
323 int ochdir(const char *dir, int options)
325 char buf[MAXPATHLEN+1];
326 char cwd[MAXPATHLEN+1];
330 if (!(options & O_NOFOLLOW))
334 dir is a canonical path (without "../" "./" "//" )
339 if (getcwd(cwd, MAXPATHLEN) == NULL)
347 * chdir request | realpath result | ret
348 * (after getwcwd) | |
349 * =======================================
352 * /a/b/. | /c/d/e/f | 1
354 if (getcwd(buf, MAXPATHLEN) == NULL)
359 /* relative path requested,
362 for (; cwd[i]; i++) {
363 if (buf[i] != cwd[i])
374 for (i = 0; test[i]; i++) {
375 if (test[i] != dir[i]) {
394 * Store n random bytes an buf
396 void randombytes(void *buf, int n)
398 char *p = (char *)buf;
402 if ((fd = open("/dev/urandom", O_RDONLY)) != -1) {
403 /* generate from /dev/urandom */
404 if (read(fd, buf, n) != n) {
409 /* fd now != -1, so srandom wont be called below */
414 gettimeofday(&tv, NULL);
415 srandom((unsigned int)tv.tv_usec);
416 for (i=0 ; i < n ; i++)
417 p[i] = random() & 0xFF;
423 int gmem(gid_t gid, int ngroups, gid_t *groups)
427 for ( i = 0; i < ngroups; i++ ) {
428 if ( groups[ i ] == gid ) {
436 * realpath() replacement that always allocates storage for returned path
438 char *realpath_safe(const char *path)
442 #ifdef REALPATH_TAKES_NULL
443 if ((resolved_path = realpath(path, NULL)) == NULL) {
444 LOG(log_debug, logtype_afpd, "realpath() cannot resolve path \"%s\"", path);
447 return resolved_path;
449 if ((resolved_path = malloc(MAXPATHLEN+1)) == NULL)
451 if (realpath(path, resolved_path) == NULL) {
453 LOG(log_debug, logtype_afpd, "realpath() cannot resolve path \"%s\"", path);
456 /* Safe some memory */
458 if ((tmp = strdup(resolved_path)) == NULL) {
464 return resolved_path;
469 * Returns pointer to static buffer with basename of path
471 const char *basename_safe(const char *path)
473 static char buf[MAXPATHLEN+1];
474 strlcpy(buf, path, MAXPATHLEN);
475 return basename(buf);
479 * extended strtok allows the quoted strings
480 * modified strtok.c in glibc 2.0.6
482 char *strtok_quote(char *s, const char *delim)
484 static char *olds = NULL;
490 /* Scan leading delimiters. */
491 s += strspn (s, delim);
495 /* Find the end of the token. */
498 if (token[0] == '\"') {
500 s = strpbrk (token, "\"");
502 s = strpbrk (token, delim);
506 /* This token finishes the string. */
507 olds = strchr (token, '\0');
509 /* Terminate the token and make OLDS point past it. */
516 int set_groups(AFPObj *obj, struct passwd *pwd)
518 if (initgroups(pwd->pw_name, pwd->pw_gid) < 0)
519 LOG(log_error, logtype_afpd, "initgroups(%s, %d): %s", pwd->pw_name, pwd->pw_gid, strerror(errno));
521 if ((obj->ngroups = getgroups(0, NULL)) < 0) {
522 LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno));
528 if (NULL == (obj->groups = calloc(obj->ngroups, sizeof(gid_t))) ) {
529 LOG(log_error, logtype_afpd, "login: %s calloc: %d", obj->ngroups);
533 if ((obj->ngroups = getgroups(obj->ngroups, obj->groups)) < 0 ) {
534 LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno));
541 #define GROUPSTR_BUFSIZE 1024
542 const char *print_groups(int ngroups, gid_t *groups)
544 static char groupsstr[GROUPSTR_BUFSIZE];
551 for (i = 0; (i < ngroups) && (s < &groupsstr[GROUPSTR_BUFSIZE]); i++) {
552 s += snprintf(s, &groupsstr[GROUPSTR_BUFSIZE] - s, " %u", groups[i]);