1/* 2 Copyright (c) 2010 Frank Lahm <franklahm@gmail.com> 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/*! 16 * @file 17 * Netatalk utility functions 18 */ 19 20#ifdef HAVE_CONFIG_H 21#include "config.h" 22#endif /* HAVE_CONFIG_H */ 23 24#include <unistd.h> 25#include <stdint.h> 26#include <errno.h> 27#include <stdlib.h> 28#include <string.h> 29#include <sys/types.h> 30#include <sys/stat.h> 31#include <fcntl.h> 32#include <dirent.h> 33#include <sys/time.h> 34#include <time.h> 35 36#include <atalk/adouble.h> 37#include <atalk/ea.h> 38#include <atalk/afp.h> 39#include <atalk/logger.h> 40#include <atalk/vfs.h> 41#include <atalk/util.h> 42#include <atalk/unix.h> 43#include <atalk/acl.h> 44 45/* close all FDs >= a specified value */ 46static void closeall(int fd) 47{ 48 int fdlimit = sysconf(_SC_OPEN_MAX); 49 50 while (fd < fdlimit) 51 close(fd++); 52} 53 54/*! 55 * Daemonize 56 * 57 * Fork, exit parent, setsid(), optionally chdir("/"), optionally close all fds 58 * 59 * returns -1 on failure, but you can't do much except exit in that case 60 * since we may already have forked 61 */ 62int daemonize(int nochdir, int noclose) 63{ 64 switch (fork()) { 65 case 0: 66 break; 67 case -1: 68 return -1; 69 default: 70 _exit(0); 71 } 72 73 if (setsid() < 0) 74 return -1; 75 76 switch (fork()) { 77 case 0: 78 break; 79 case -1: 80 return -1; 81 default: 82 _exit(0); 83 } 84 85 if (!nochdir) 86 chdir("/"); 87 88 if (!noclose) { 89 closeall(0); 90 open("/dev/null",O_RDWR); 91 dup(0); 92 dup(0); 93 } 94 95 return 0; 96} 97 98static uid_t saved_uid = -1; 99 100void become_root(void) 101{ 102 saved_uid = geteuid(); 103 if (seteuid(0) != 0) 104 AFP_PANIC("Can't seteuid(0)"); 105} 106 107void unbecome_root(void) 108{ 109 if (saved_uid == -1 || seteuid(saved_uid) < 0) 110 AFP_PANIC("Can't seteuid back"); 111 saved_uid = -1; 112} 113 114/*! 115 * @brief get cwd in static buffer 116 * 117 * @returns pointer to path or pointer to error messages on error 118 */ 119const char *getcwdpath(void) 120{ 121 static char cwd[MAXPATHLEN + 1]; 122 char *p; 123 124 if ((p = getcwd(cwd, MAXPATHLEN)) != NULL) 125 return p; 126 else 127 return strerror(errno); 128} 129 130/*! 131 * @brief Request absolute path 132 * 133 * @returns Absolute filesystem path to object 134 */ 135const char *fullpathname(const char *name) 136{ 137 static char wd[MAXPATHLEN + 1]; 138 139 if (name[0] == '/') 140 return name; 141 142 if (getcwd(wd , MAXPATHLEN)) { 143 strlcat(wd, "/", MAXPATHLEN); 144 strlcat(wd, name, MAXPATHLEN); 145 } else { 146 strlcpy(wd, name, MAXPATHLEN); 147 } 148 149 return wd; 150} 151 152/*! 153 * Takes a buffer with a path, strips slashs, returns basename 154 * 155 * @param p (rw) path 156 * path may be 157 * "[/][dir/[...]]file" 158 * or 159 * "[/][dir/[...]]dir/[/]" 160 * Result is "file" or "dir" 161 * 162 * @returns pointer to basename in path buffer, buffer is possibly modified 163 */ 164char *stripped_slashes_basename(char *p) 165{ 166 int i = strlen(p) - 1; 167 while (i > 0 && p[i] == '/') 168 p[i--] = 0; 169 return (strrchr(p, '/') ? strrchr(p, '/') + 1 : p); 170} 171 172/********************************************************************************* 173 * chdir(), chmod(), chown(), stat() wrappers taking an additional option. 174 * Currently the only used options are O_NOFOLLOW, used to switch between symlink 175 * behaviour, and O_NETATALK_ACL for ochmod() indicating chmod_acl() shall be 176 * called which does special ACL handling depending on the filesytem 177 *********************************************************************************/ 178 179int ostat(const char *path, struct stat *buf, int options) 180{ 181 if (options & O_NOFOLLOW) 182 return lstat(path, buf); 183 else 184 return stat(path, buf); 185} 186 187int ochown(const char *path, uid_t owner, gid_t group, int options) 188{ 189 if (options & O_NOFOLLOW) 190 return lchown(path, owner, group); 191 else 192 return chown(path, owner, group); 193} 194 195/*! 196 * chmod() wrapper for symlink and ACL handling 197 * 198 * @param path (r) path 199 * @param mode (r) requested mode 200 * @param sb (r) stat() of path or NULL 201 * @param option (r) O_NOFOLLOW | O_NETATALK_ACL 202 * 203 * Options description: 204 * O_NOFOLLOW: don't chmod() symlinks, do nothing, return 0 205 * O_NETATALK_ACL: call chmod_acl() instead of chmod() 206 */ 207int ochmod(const char *path, mode_t mode, const struct stat *st, int options) 208{ 209 struct stat sb; 210 211 if (!st) { 212 if (lstat(path, &sb) != 0) 213 return -1; 214 st = &sb; 215 } 216 217 if (options & O_NOFOLLOW) 218 if (S_ISLNK(st->st_mode)) 219 return 0; 220 221#ifdef HAVE_ACLS 222 if (options & O_NETATALK_ACL) { 223 return chmod_acl(path, mode); 224 } else { 225#endif 226 return chmod(path, mode); 227#ifdef HAVE_ACLS 228 } 229#endif 230} 231 232/* 233 * @brief ostat/fsstatat multiplexer 234 * 235 * ostatat mulitplexes ostat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored. 236 * 237 * @param dirfd (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD 238 * @param path (r) pathname 239 * @param st (rw) pointer to struct stat 240 */ 241int ostatat(int dirfd, const char *path, struct stat *st, int options) 242{ 243#ifdef HAVE_ATFUNCS 244 if (dirfd == -1) 245 dirfd = AT_FDCWD; 246 return fstatat(dirfd, path, st, (options & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); 247#else 248 return ostat(path, st, options); 249#endif 250 251 /* DEADC0DE */ 252 return -1; 253} 254 255/*! 256 * @brief symlink safe chdir replacement 257 * 258 * Only chdirs to dir if it doesn't contain symlinks or if symlink checking 259 * is disabled 260 * 261 * @returns 1 if a path element is a symlink, 0 otherwise, -1 on syserror 262 */ 263int ochdir(const char *dir, int options) 264{ 265 char buf[MAXPATHLEN+1]; 266 char cwd[MAXPATHLEN+1]; 267 char *test; 268 int i; 269 270 if (!(options & O_NOFOLLOW)) 271 return chdir(dir); 272 273 /* 274 dir is a canonical path (without "../" "./" "//" ) 275 but may end with a / 276 */ 277 *cwd = 0; 278 if (*dir != '/') { 279 if (getcwd(cwd, MAXPATHLEN) == NULL) 280 return -1; 281 } 282 if (chdir(dir) != 0) 283 return -1; 284 285 /* 286 * Cases: 287 * chdir request | realpath result | ret 288 * (after getwcwd) | | 289 * ======================================= 290 * /a/b/. | /a/b | 0 291 * /a/b/. | /c | 1 292 * /a/b/. | /c/d/e/f | 1 293 */ 294 if (getcwd(buf, MAXPATHLEN) == NULL) 295 return 1; 296 297 i = 0; 298 if (*cwd) { 299 /* relative path requested, 300 * Same directory? 301 */ 302 for (; cwd[i]; i++) { 303 if (buf[i] != cwd[i]) 304 return 1; 305 } 306 if (buf[i]) { 307 if (buf[i] != '/') 308 return 1; 309 i++; 310 } 311 } 312 313 test = &buf[i]; 314 for (i = 0; test[i]; i++) { 315 if (test[i] != dir[i]) { 316 return 1; 317 } 318 } 319 /* trailing '/' ? */ 320 if (!dir[i]) 321 return 0; 322 323 if (dir[i] != '/') 324 return 1; 325 326 i++; 327 if (dir[i]) 328 return 1; 329 330 return 0; 331} 332 333/*! 334 * Store n random bytes an buf 335 */ 336void randombytes(void *buf, int n) 337{ 338 char *p = (char *)buf; 339 int fd, i; 340 struct timeval tv; 341 342 if ((fd = open("/dev/urandom", O_RDONLY)) != -1) { 343 /* generate from /dev/urandom */ 344 if (read(fd, buf, n) != n) { 345 close(fd); 346 fd = -1; 347 } else { 348 close(fd); 349 /* fd now != -1, so srandom wont be called below */ 350 } 351 } 352 353 if (fd == -1) { 354 gettimeofday(&tv, NULL); 355 srandom((unsigned int)tv.tv_usec); 356 for (i=0 ; i < n ; i++) 357 p[i] = random() & 0xFF; 358 } 359 360 return; 361} 362