1/* 2 * Copyright (c) 1990,1993 Regents of The University of Michigan. 3 * All Rights Reserved. See COPYRIGHT. 4 */ 5 6#ifdef HAVE_CONFIG_H 7#include "config.h" 8#endif /* HAVE_CONFIG_H */ 9 10#include <unistd.h> 11#include <errno.h> 12#include <stdlib.h> 13#include <sys/param.h> 14#include <sys/types.h> 15#include <sys/stat.h> 16#include <string.h> 17 18#include <atalk/afp.h> 19#include <atalk/util.h> 20#include <atalk/directory.h> 21#include <atalk/volume.h> 22#include <atalk/logger.h> 23#include <atalk/unix.h> 24#include <atalk/acl.h> 25 26/* ----------------------------- 27 a dropbox is a folder where w is set but not r eg: 28 rwx-wx-wx or rwx-wx-- 29 rwx----wx (is not asked by a Mac with OS >= 8.0 ?) 30 using chmod_acl() instead of ochmod is ok here 31*/ 32int stickydirmode(const char *name, const mode_t mode, const int dropbox, const mode_t v_umask) 33{ 34 int retval = 0; 35 36#ifdef DROPKLUDGE 37 /* Turn on the sticky bit if this is a drop box, also turn off the setgid bit */ 38 if ((dropbox & AFPVOL_DROPBOX)) { 39 int uid; 40 41 if ( ( (mode & S_IWOTH) && !(mode & S_IROTH)) || 42 ( (mode & S_IWGRP) && !(mode & S_IRGRP)) ) 43 { 44 uid=geteuid(); 45 if ( seteuid(0) < 0) { 46 LOG(log_error, logtype_afpd, "stickydirmode: unable to seteuid root: %s", strerror(errno)); 47 } 48 if ( (retval=chmod_acl( name, ( (DIRBITS | mode | S_ISVTX) & ~v_umask) )) < 0) { 49 LOG(log_error, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(errno) ); 50 } else { 51 LOG(log_debug, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(retval) ); 52 } 53 seteuid(uid); 54 return retval; 55 } 56 } 57#endif /* DROPKLUDGE */ 58 59 /* 60 * Ignore EPERM errors: We may be dealing with a directory that is 61 * group writable, in which case chmod will fail. 62 */ 63 if ( (chmod_acl( name, (DIRBITS | mode) & ~v_umask ) < 0) && errno != EPERM && 64 !(errno == ENOENT && (dropbox & AFPVOL_NOADOUBLE)) ) 65 { 66 LOG(log_error, logtype_afpd, "stickydirmode: chmod \"%s\": %s", fullpathname(name), strerror(errno) ); 67 retval = -1; 68 } 69 70 return retval; 71} 72 73/* ------------------------- */ 74int dir_rx_set(mode_t mode) 75{ 76 return (mode & (S_IXUSR | S_IRUSR)) == (S_IXUSR | S_IRUSR); 77} 78 79/* --------------------- */ 80int setfilmode(const struct vol *vol, const char *name, mode_t mode, struct stat *st) 81{ 82 struct stat sb; 83 mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO; /* rwx for owner group and other, by default */ 84 85 if (!st) { 86 if (lstat(name, &sb) != 0) 87 return -1; 88 st = &sb; 89 } 90 91 mode |= st->st_mode & ~mask; /* keep other bits from previous mode */ 92 93#ifdef HAVE_ACLS 94 if (ochmod(name, mode & ~vol->v_umask, st, vol_syml_opt(vol) | O_NETATALK_ACL) < 0 && errno != EPERM ) { 95 return -1; 96 } 97#else 98 if (ochmod(name, mode & ~vol->v_umask, st, vol_syml_opt(vol)) < 0 && errno != EPERM ) { 99 return -1; 100 } 101#endif 102 return 0; 103} 104 105/* 106 * @brief system rmdir with afp error code. 107 * 108 * Supports *at semantics (cf openat) if HAVE_ATFUNCS. Pass dirfd=-1 to ignore this. 109 */ 110int netatalk_rmdir_all_errors(int dirfd, const char *name) 111{ 112 int err; 113 114#ifdef HAVE_ATFUNCS 115 if (dirfd == -1) 116 dirfd = AT_FDCWD; 117 err = unlinkat(dirfd, name, AT_REMOVEDIR); 118#else 119 err = rmdir(name); 120#endif 121 122 if (err < 0) { 123 switch ( errno ) { 124 case ENOENT : 125 return AFPERR_NOOBJ; 126 case ENOTEMPTY : 127 return AFPERR_DIRNEMPT; 128 case EPERM: 129 case EACCES : 130 return AFPERR_ACCESS; 131 case EROFS: 132 return AFPERR_VLOCK; 133 default : 134 return AFPERR_PARAM; 135 } 136 } 137 return AFP_OK; 138} 139 140/* 141 * @brief System rmdir with afp error code, but ENOENT is not an error. 142 * 143 * Supports *at semantics (cf openat) if HAVE_ATFUNCS. Pass dirfd=-1 to ignore this. 144 */ 145int netatalk_rmdir(int dirfd, const char *name) 146{ 147 int ret = netatalk_rmdir_all_errors(dirfd, name); 148 if (ret == AFPERR_NOOBJ) 149 return AFP_OK; 150 return ret; 151} 152 153/* ------------------- 154 system unlink with afp error code. 155 ENOENT is not an error. 156*/ 157int netatalk_unlink(const char *name) 158{ 159 if (unlink(name) < 0) { 160 switch (errno) { 161 case ENOENT : 162 break; 163 case EROFS: 164 return AFPERR_VLOCK; 165 case EPERM: 166 case EACCES : 167 return AFPERR_ACCESS; 168 default : 169 return AFPERR_PARAM; 170 } 171 } 172 return AFP_OK; 173} 174 175/************************************************************************** 176 * *at semnatics support functions (like openat, renameat standard funcs) 177 **************************************************************************/ 178 179/* 180 * Supports *at semantics if HAVE_ATFUNCS, pass dirfd=-1 to ignore this 181 */ 182int copy_file(int dirfd, const char *src, const char *dst, mode_t mode) 183{ 184 int ret = 0; 185 int sfd = -1; 186 int dfd = -1; 187 ssize_t cc; 188 size_t buflen; 189 char filebuf[8192]; 190 191#ifdef HAVE_ATFUNCS 192 if (dirfd == -1) 193 dirfd = AT_FDCWD; 194 sfd = openat(dirfd, src, O_RDONLY); 195#else 196 sfd = open(src, O_RDONLY); 197#endif 198 if (sfd < 0) { 199 LOG(log_info, logtype_afpd, "copy_file('%s'/'%s'): open '%s' error: %s", 200 src, dst, src, strerror(errno)); 201 return -1; 202 } 203 204 if ((dfd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, mode)) < 0) { 205 LOG(log_info, logtype_afpd, "copy_file('%s'/'%s'): open '%s' error: %s", 206 src, dst, dst, strerror(errno)); 207 ret = -1; 208 goto exit; 209 } 210 211 while ((cc = read(sfd, filebuf, sizeof(filebuf)))) { 212 if (cc < 0) { 213 if (errno == EINTR) 214 continue; 215 LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): read '%s' error: %s", 216 src, dst, src, strerror(errno)); 217 ret = -1; 218 goto exit; 219 } 220 221 buflen = cc; 222 while (buflen > 0) { 223 if ((cc = write(dfd, filebuf, buflen)) < 0) { 224 if (errno == EINTR) 225 continue; 226 LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): read '%s' error: %s", 227 src, dst, dst, strerror(errno)); 228 ret = -1; 229 goto exit; 230 } 231 buflen -= cc; 232 } 233 } 234 235exit: 236 if (sfd != -1) 237 close(sfd); 238 239 if (dfd != -1) { 240 int err; 241 242 err = close(dfd); 243 if (!ret && err) { 244 /* don't bother to report an error if there's already one */ 245 LOG(log_error, logtype_afpd, "copy_file('%s'/'%s'): close '%s' error: %s", 246 src, dst, dst, strerror(errno)); 247 ret = -1; 248 } 249 } 250 251 return ret; 252} 253 254/* 255 * at wrapper for netatalk_unlink 256 */ 257int netatalk_unlinkat(int dirfd, const char *name) 258{ 259#ifdef HAVE_ATFUNCS 260 if (dirfd == -1) 261 dirfd = AT_FDCWD; 262 263 if (unlinkat(dirfd, name, 0) < 0) { 264 switch (errno) { 265 case ENOENT : 266 break; 267 case EROFS: 268 return AFPERR_VLOCK; 269 case EPERM: 270 case EACCES : 271 return AFPERR_ACCESS; 272 default : 273 return AFPERR_PARAM; 274 } 275 } 276 return AFP_OK; 277#else 278 return netatalk_unlink(name); 279#endif 280 281 /* DEADC0DE */ 282 return 0; 283} 284 285/* 286 * @brief This is equivalent of unix rename() 287 * 288 * unix_rename mulitplexes rename and renameat. If we dont HAVE_ATFUNCS, sfd and dfd 289 * are ignored. 290 * 291 * @param sfd (r) if we HAVE_ATFUNCS, -1 gives AT_FDCWD 292 * @param oldpath (r) guess what 293 * @param dfd (r) same as sfd 294 * @param newpath (r) guess what 295 */ 296int unix_rename(int sfd, const char *oldpath, int dfd, const char *newpath) 297{ 298#ifdef HAVE_ATFUNCS 299 if (sfd == -1) 300 sfd = AT_FDCWD; 301 if (dfd == -1) 302 dfd = AT_FDCWD; 303 304 if (renameat(sfd, oldpath, dfd, newpath) < 0) 305 return -1; 306#else 307 if (rename(oldpath, newpath) < 0) 308 return -1; 309#endif /* HAVE_ATFUNCS */ 310 311 return 0; 312} 313 314/* 315 * @brief stat/fsstatat multiplexer 316 * 317 * statat mulitplexes stat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored. 318 * 319 * @param dirfd (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD 320 * @param path (r) pathname 321 * @param st (rw) pointer to struct stat 322 */ 323int statat(int dirfd, const char *path, struct stat *st) 324{ 325#ifdef HAVE_ATFUNCS 326 if (dirfd == -1) 327 dirfd = AT_FDCWD; 328 return (fstatat(dirfd, path, st, 0)); 329#else 330 return (stat(path, st)); 331#endif 332 333 /* DEADC0DE */ 334 return -1; 335} 336 337/* 338 * @brief opendir wrapper for *at semantics support 339 * 340 * opendirat chdirs to dirfd if dirfd != -1 before calling opendir on path. 341 * 342 * @param dirfd (r) if != -1, chdir(dirfd) before opendir(path) 343 * @param path (r) pathname 344 */ 345DIR *opendirat(int dirfd, const char *path) 346{ 347 DIR *ret; 348 int cwd = -1; 349 350 if (dirfd != -1) { 351 if (((cwd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) { 352 ret = NULL; 353 goto exit; 354 } 355 } 356 357 ret = opendir(path); 358 359 if (dirfd != -1 && fchdir(cwd) != 0) { 360 LOG(log_error, logtype_afpd, "opendirat: cant chdir back. exit!"); 361 exit(EXITERR_SYS); 362 } 363 364exit: 365 if (cwd != -1) 366 close(cwd); 367 368 return ret; 369} 370