1/* 2 $Id: ea_sys.c,v 1.8 2010-04-13 08:05:06 franklahm Exp $ 3 Copyright (c) 2009 Frank Lahm <franklahm@gmail.com> 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14*/ 15 16#ifdef HAVE_CONFIG_H 17#include "config.h" 18#endif /* HAVE_CONFIG_H */ 19 20#include <unistd.h> 21#include <stdint.h> 22#include <errno.h> 23#include <stdlib.h> 24#include <string.h> 25#include <sys/types.h> 26#include <sys/stat.h> 27#include <fcntl.h> 28#include <dirent.h> 29 30#if HAVE_ATTR_XATTR_H 31#include <attr/xattr.h> 32#elif HAVE_SYS_XATTR_H 33#include <sys/xattr.h> 34#endif 35 36#ifdef HAVE_SYS_EA_H 37#include <sys/ea.h> 38#endif 39 40#ifdef HAVE_SYS_EXTATTR_H 41#include <sys/extattr.h> 42#endif 43 44#include <atalk/adouble.h> 45#include <atalk/ea.h> 46#include <atalk/afp.h> 47#include <atalk/logger.h> 48#include <atalk/volume.h> 49#include <atalk/vfs.h> 50#include <atalk/util.h> 51#include <atalk/unix.h> 52#include <atalk/compat.h> 53 54#ifndef ENOATTR 55#define ENOATTR ENODATA 56#endif 57 58 59/********************************************************************************** 60 * EA VFS funcs for storing EAs in nativa filesystem EAs 61 **********************************************************************************/ 62 63/* 64 * Function: sys_get_easize 65 * 66 * Purpose: get size of a native EA 67 * 68 * Arguments: 69 * 70 * vol (r) current volume 71 * rbuf (w) DSI reply buffer 72 * rbuflen (rw) current length of data in reply buffer 73 * uname (r) filename 74 * oflag (r) link and create flag 75 * attruname (r) name of attribute 76 * 77 * Returns: AFP code: AFP_OK on success or appropiate AFP error code 78 * 79 * Effects: 80 * 81 * Copies EA size into rbuf in network order. Increments *rbuflen +4. 82 */ 83int sys_get_easize(VFS_FUNC_ARGS_EA_GETSIZE) 84{ 85 ssize_t ret; 86 uint32_t attrsize; 87 88 LOG(log_debug7, logtype_afpd, "sys_getextattr_size(%s): attribute: \"%s\"", uname, attruname); 89 90 if ((oflag & O_NOFOLLOW) ) { 91 ret = sys_lgetxattr(uname, attruname, rbuf +4, 0); 92 } 93 else { 94 ret = sys_getxattr(uname, attruname, rbuf +4, 0); 95 } 96 97 if (ret == -1) { 98 memset(rbuf, 0, 4); 99 *rbuflen += 4; 100 switch(errno) { 101 case OPEN_NOFOLLOW_ERRNO: 102 /* its a symlink and client requested O_NOFOLLOW */ 103 LOG(log_debug, logtype_afpd, "sys_getextattr_size(%s): encountered symlink with kXAttrNoFollow", uname); 104 return AFP_OK; 105 106 case ENOATTR: 107 return AFPERR_MISC; 108 109 default: 110 LOG(log_error, logtype_afpd, "sys_getextattr_size: error: %s", strerror(errno)); 111 return AFPERR_MISC; 112 } 113 } 114 115 if (ret > MAX_EA_SIZE) 116 ret = MAX_EA_SIZE; 117 118 /* Start building reply packet */ 119 LOG(log_debug7, logtype_afpd, "sys_getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, ret); 120 121 /* length of attribute data */ 122 attrsize = htonl((uint32_t)ret); 123 memcpy(rbuf, &attrsize, 4); 124 *rbuflen += 4; 125 126 ret = AFP_OK; 127 return ret; 128} 129 130/* 131 * Function: sys_get_eacontent 132 * 133 * Purpose: copy native EA into rbuf 134 * 135 * Arguments: 136 * 137 * vol (r) current volume 138 * rbuf (w) DSI reply buffer 139 * rbuflen (rw) current length of data in reply buffer 140 * uname (r) filename 141 * oflag (r) link and create flag 142 * attruname (r) name of attribute 143 * maxreply (r) maximum EA size as of current specs/real-life 144 * 145 * Returns: AFP code: AFP_OK on success or appropiate AFP error code 146 * 147 * Effects: 148 * 149 * Copies EA into rbuf. Increments *rbuflen accordingly. 150 */ 151int sys_get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT) 152{ 153 ssize_t ret; 154 uint32_t attrsize; 155 156 /* Start building reply packet */ 157 158 maxreply -= MAX_REPLY_EXTRA_BYTES; 159 160 if (maxreply > MAX_EA_SIZE) 161 maxreply = MAX_EA_SIZE; 162 163 LOG(log_debug7, logtype_afpd, "sys_getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply); 164 165 if ((oflag & O_NOFOLLOW) ) { 166 ret = sys_lgetxattr(uname, attruname, rbuf +4, maxreply); 167 } 168 else { 169 ret = sys_getxattr(uname, attruname, rbuf +4, maxreply); 170 } 171 172 if (ret == -1) { 173 memset(rbuf, 0, 4); 174 *rbuflen += 4; 175 switch(errno) { 176 case OPEN_NOFOLLOW_ERRNO: 177 /* its a symlink and client requested O_NOFOLLOW */ 178 LOG(log_debug, logtype_afpd, "sys_getextattr_content(%s): encountered symlink with kXAttrNoFollow", uname); 179 return AFP_OK; 180 181 case ENOATTR: 182 return AFPERR_MISC; 183 184 default: 185 LOG(log_error, logtype_afpd, "sys_getextattr_content(%s): error: %s", attruname, strerror(errno)); 186 return AFPERR_MISC; 187 } 188 } 189 190 /* remember where we must store length of attribute data in rbuf */ 191 *rbuflen += 4 +ret; 192 193 attrsize = htonl((uint32_t)ret); 194 memcpy(rbuf, &attrsize, 4); 195 196 return AFP_OK; 197} 198 199/* 200 * Function: sys_list_eas 201 * 202 * Purpose: copy names of native EAs into attrnamebuf 203 * 204 * Arguments: 205 * 206 * vol (r) current volume 207 * attrnamebuf (w) store names a consecutive C strings here 208 * buflen (rw) length of names in attrnamebuf 209 * uname (r) filename 210 * oflag (r) link and create flag 211 * 212 * Returns: AFP code: AFP_OK on success or appropiate AFP error code 213 * 214 * Effects: 215 * 216 * Copies names of all EAs of uname as consecutive C strings into rbuf. 217 * Increments *rbuflen accordingly. 218 */ 219int sys_list_eas(VFS_FUNC_ARGS_EA_LIST) 220{ 221 ssize_t attrbuflen = *buflen; 222 int ret, len, nlen; 223 char *buf; 224 char *ptr; 225 226 buf = malloc(ATTRNAMEBUFSIZ); 227 if (!buf) 228 return AFPERR_MISC; 229 230 if ((oflag & O_NOFOLLOW)) { 231 ret = sys_llistxattr(uname, buf, ATTRNAMEBUFSIZ); 232 } 233 else { 234 ret = sys_listxattr(uname, buf, ATTRNAMEBUFSIZ); 235 } 236 237 if (ret == -1) switch(errno) { 238 case OPEN_NOFOLLOW_ERRNO: 239 /* its a symlink and client requested O_NOFOLLOW */ 240 ret = AFPERR_BADTYPE; 241 goto exit; 242#ifdef HAVE_ATTROPEN /* Solaris */ 243 case ENOATTR: 244 ret = AFP_OK; 245 goto exit; 246#endif 247 default: 248 LOG(log_error, logtype_afpd, "sys_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno)); 249 ret= AFPERR_MISC; 250 goto exit; 251 } 252 253 ptr = buf; 254 while (ret > 0) { 255 len = strlen(ptr); 256 257 /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */ 258 if ( 0 >= ( nlen = convert_string(vol->v_volcharset, CH_UTF8_MAC, ptr, len, attrnamebuf + attrbuflen, 256)) ) { 259 ret = AFPERR_MISC; 260 goto exit; 261 } 262 263 LOG(log_debug7, logtype_afpd, "sys_list_extattr(%s): attribute: %s", uname, ptr); 264 265 attrbuflen += nlen + 1; 266 if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) { 267 /* Next EA name could overflow, so bail out with error. 268 FIXME: evantually malloc/memcpy/realloc whatever. 269 Is it worth it ? */ 270 LOG(log_warning, logtype_afpd, "sys_list_extattr(%s): running out of buffer for EA names", uname); 271 ret = AFPERR_MISC; 272 goto exit; 273 } 274 ret -= len +1; 275 ptr += len +1; 276 } 277 278 ret = AFP_OK; 279 280exit: 281 free(buf); 282 *buflen = attrbuflen; 283 return ret; 284} 285 286/* 287 * Function: sys_set_ea 288 * 289 * Purpose: set a native EA 290 * 291 * Arguments: 292 * 293 * vol (r) current volume 294 * uname (r) filename 295 * attruname (r) EA name 296 * ibuf (r) buffer with EA content 297 * attrsize (r) length EA in ibuf 298 * oflag (r) link and create flag 299 * 300 * Returns: AFP code: AFP_OK on success or appropiate AFP error code 301 * 302 * Effects: 303 * 304 */ 305int sys_set_ea(VFS_FUNC_ARGS_EA_SET) 306{ 307 int attr_flag; 308 int ret; 309 310 attr_flag = 0; 311 if ((oflag & O_CREAT) ) 312 attr_flag |= XATTR_CREATE; 313 314 else if ((oflag & O_TRUNC) ) 315 attr_flag |= XATTR_REPLACE; 316 317 if ((oflag & O_NOFOLLOW) ) { 318 ret = sys_lsetxattr(uname, attruname, ibuf, attrsize,attr_flag); 319 } 320 else { 321 ret = sys_setxattr(uname, attruname, ibuf, attrsize, attr_flag); 322 } 323 324 if (ret == -1) { 325 switch(errno) { 326 case OPEN_NOFOLLOW_ERRNO: 327 /* its a symlink and client requested O_NOFOLLOW */ 328 LOG(log_debug, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s'): encountered symlink with kXAttrNoFollow", 329 getcwdpath(), uname, attruname); 330 return AFP_OK; 331 case EEXIST: 332 LOG(log_debug, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s'): EA already exists", 333 getcwdpath(), uname, attruname); 334 return AFPERR_EXIST; 335 default: 336 LOG(log_error, logtype_afpd, "sys_set_ea(\"%s/%s\", ea:'%s', size: %u, flags: %s|%s|%s): %s", 337 getcwdpath(), uname, attruname, attrsize, 338 oflag & O_CREAT ? "XATTR_CREATE" : "-", 339 oflag & O_TRUNC ? "XATTR_REPLACE" : "-", 340 oflag & O_NOFOLLOW ? "O_NOFOLLOW" : "-", 341 strerror(errno)); 342 return AFPERR_MISC; 343 } 344 } 345 346 return AFP_OK; 347} 348 349/* 350 * Function: sys_remove_ea 351 * 352 * Purpose: remove a native EA 353 * 354 * Arguments: 355 * 356 * vol (r) current volume 357 * uname (r) filename 358 * attruname (r) EA name 359 * oflag (r) link and create flag 360 * 361 * Returns: AFP code: AFP_OK on success or appropiate AFP error code 362 * 363 * Effects: 364 * 365 * Removes EA attruname from file uname. 366 */ 367int sys_remove_ea(VFS_FUNC_ARGS_EA_REMOVE) 368{ 369 int ret; 370 371 if ((oflag & O_NOFOLLOW) ) { 372 ret = sys_lremovexattr(uname, attruname); 373 } 374 else { 375 ret = sys_removexattr(uname, attruname); 376 } 377 378 if (ret == -1) { 379 switch(errno) { 380 case OPEN_NOFOLLOW_ERRNO: 381 /* its a symlink and client requested O_NOFOLLOW */ 382 LOG(log_debug, logtype_afpd, "sys_remove_ea(%s/%s): encountered symlink with kXAttrNoFollow", uname); 383 return AFP_OK; 384 case EACCES: 385 LOG(log_debug, logtype_afpd, "sys_remove_ea(%s/%s): error: %s", uname, attruname, strerror(errno)); 386 return AFPERR_ACCESS; 387 default: 388 LOG(log_error, logtype_afpd, "sys_remove_ea(%s/%s): error: %s", uname, attruname, strerror(errno)); 389 return AFPERR_MISC; 390 } 391 } 392 393 return AFP_OK; 394} 395 396/* 397 * @brief Copy EAs 398 * 399 * @note Supports *at semantics, therfor switches back and forth between sfd and cwd 400 */ 401int sys_ea_copyfile(VFS_FUNC_ARGS_COPYFILE) 402{ 403 int ret = 0; 404 int cwd = -1; 405 ssize_t size; 406 char *names = NULL, *end_names, *name, *value = NULL; 407 unsigned int setxattr_ENOTSUP = 0; 408 409 if (sfd != -1) { 410 if ((cwd = open(".", O_RDONLY)) == -1) { 411 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant open cwd: %s", 412 strerror(errno)); 413 ret = -1; 414 goto getout; 415 } 416 } 417 418 if (sfd != -1) { 419 if (fchdir(sfd) == -1) { 420 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s", 421 strerror(errno)); 422 ret = -1; 423 goto getout; 424 } 425 } 426 427 size = sys_listxattr(src, NULL, 0); 428 if (size < 0) { 429 if (errno != ENOSYS && errno != ENOTSUP) { 430 ret = -1; 431 } 432 goto getout; 433 } 434 names = malloc(size+1); 435 if (names == NULL) { 436 ret = -1; 437 goto getout; 438 } 439 size = sys_listxattr(src, names, size); 440 if (size < 0) { 441 ret = -1; 442 goto getout; 443 } else { 444 names[size] = '\0'; 445 end_names = names + size; 446 } 447 448 if (sfd != -1) { 449 if (fchdir(cwd) == -1) { 450 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s", 451 strerror(errno)); 452 ret = -1; 453 goto getout; 454 } 455 } 456 457 for (name = names; name != end_names; name = strchr(name, '\0') + 1) { 458 void *old_value; 459 460 /* check if this attribute shall be preserved */ 461 if (!*name) 462 continue; 463 464 if (sfd != -1) { 465 if (fchdir(sfd) == -1) { 466 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to sfd: %s", 467 strerror(errno)); 468 ret = -1; 469 goto getout; 470 } 471 } 472 473 size = sys_getxattr (src, name, NULL, 0); 474 if (size < 0) { 475 ret = -1; 476 continue; 477 } 478 value = realloc(old_value = value, size); 479 if (size != 0 && value == NULL) { 480 free(old_value); 481 ret = -1; 482 } 483 size = sys_getxattr(src, name, value, size); 484 if (size < 0) { 485 ret = -1; 486 continue; 487 } 488 489 if (sfd != -1) { 490 if (fchdir(cwd) == -1) { 491 LOG(log_error, logtype_afpd, "sys_ea_copyfile: cant chdir to cwd: %s", 492 strerror(errno)); 493 ret = -1; 494 goto getout; 495 } 496 } 497 498 if (sys_setxattr(dst, name, value, size, 0) != 0) { 499 if (errno == ENOTSUP) 500 setxattr_ENOTSUP++; 501 else { 502 if (errno == ENOSYS) { 503 ret = -1; 504 /* no hope of getting any further */ 505 break; 506 } else { 507 ret = -1; 508 } 509 } 510 } 511 } 512 if (setxattr_ENOTSUP) { 513 errno = ENOTSUP; 514 ret = -1; 515 } 516 517getout: 518 if (cwd != -1) 519 close(cwd); 520 521 free(value); 522 free(names); 523 524 if (ret == -1) { 525 switch(errno) { 526 case ENOENT: 527 /* no attribute */ 528 break; 529 case EACCES: 530 LOG(log_debug, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno)); 531 return AFPERR_ACCESS; 532 default: 533 LOG(log_error, logtype_afpd, "sys_ea_copyfile(%s, %s): error: %s", src, dst, strerror(errno)); 534 return AFPERR_MISC; 535 } 536 } 537 538 return AFP_OK; 539} 540