1/* 2 $Id: extattrs.c,v 1.29 2010-01-05 12:06:33 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 <errno.h> 22#include <stdlib.h> 23#include <string.h> 24 25#include <atalk/adouble.h> 26#include <atalk/util.h> 27#include <atalk/vfs.h> 28#include <atalk/afp.h> 29#include <atalk/logger.h> 30#include <atalk/ea.h> 31#include <atalk/globals.h> 32 33#include "volume.h" 34#include "desktop.h" 35#include "directory.h" 36#include "fork.h" 37#include "extattrs.h" 38 39static const char *ea_finderinfo = "com.apple.FinderInfo"; 40static const char *ea_resourcefork = "com.apple.ResourceFork"; 41 42/* This should be big enough to consecutively store the names of all attributes */ 43static char attrnamebuf[ATTRNAMEBUFSIZ]; 44 45#ifdef DEBUG 46static void hexdump(void *m, size_t l) { 47 char *p = m; 48 int count = 0, len; 49 char buf[100]; 50 char *bufp = buf; 51 52 while (l--) { 53 len = sprintf(bufp, "%02x ", *p++); 54 bufp += len; 55 count++; 56 57 if ((count % 16) == 0) { 58 LOG(log_debug9, logtype_afpd, "%s", buf); 59 bufp = buf; 60 } 61 } 62} 63#endif 64 65/*************************************** 66 * AFP funcs 67 ****************************************/ 68 69/* 70 Note: we're being called twice. Firstly the client only want the size of all 71 EA names, secondly it wants these names. In order to avoid scanning EAs twice 72 we cache them in a static buffer. 73*/ 74int afp_listextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen) 75{ 76 int ret, oflag = 0, adflags = 0; 77 uint16_t vid, bitmap, uint16; 78 uint32_t did, maxreply, tmpattr; 79 struct vol *vol; 80 struct dir *dir; 81 struct path *s_path; 82 struct stat *st; 83 struct adouble ad, *adp = NULL; 84 char *uname, *FinderInfo; 85 char emptyFinderInfo[32] = { 0 }; 86 87 static int buf_valid = 0; 88 static size_t attrbuflen = 0; 89 90 *rbuflen = 0; 91 ibuf += 2; 92 93 /* Get Bitmap and MaxReplySize first */ 94 memcpy( &bitmap, ibuf +6, sizeof(bitmap)); 95 bitmap = ntohs( bitmap ); 96 97 memcpy( &maxreply, ibuf + 14, sizeof (maxreply)); 98 maxreply = ntohl( maxreply ); 99 100 /* 101 If its the first request with maxreply=0 or if we didn't mark our buffers valid for 102 whatever reason (just a safety check, it should be valid), then scan for attributes 103 */ 104 if ((maxreply == 0) || (buf_valid == 0)) { 105 106 attrbuflen = 0; 107 108 memcpy( &vid, ibuf, sizeof(vid)); 109 ibuf += sizeof(vid); 110 if (NULL == ( vol = getvolbyvid( vid )) ) { 111 LOG(log_debug, logtype_afpd, "afp_listextattr: getvolbyvid error: %s", strerror(errno)); 112 return AFPERR_ACCESS; 113 } 114 115 memcpy( &did, ibuf, sizeof(did)); 116 ibuf += sizeof(did); 117 if (NULL == ( dir = dirlookup( vol, did )) ) { 118 LOG(log_debug, logtype_afpd, "afp_listextattr: dirlookup error: %s", strerror(errno)); 119 return afp_errno; 120 } 121 122 if (bitmap & kXAttrNoFollow) 123 oflag = O_NOFOLLOW; 124 /* Skip Bitmap, ReqCount, StartIndex and maxreply*/ 125 ibuf += 12; 126 127 /* get name */ 128 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) { 129 LOG(log_debug, logtype_afpd, "afp_listextattr: cname error: %s", strerror(errno)); 130 return AFPERR_NOOBJ; 131 } 132 133 st = &s_path->st; 134 if (!s_path->st_valid) { 135 /* it's a dir in our cache, we didn't stat it, do it now */ 136 of_statdir(vol, s_path); 137 } 138 if ( s_path->st_errno != 0 ) { 139 return( AFPERR_NOOBJ ); 140 } 141 142 adp = of_ad(vol, s_path, &ad); 143 uname = s_path->u_name; 144 /* 145 We have to check the FinderInfo for the file, because if they aren't all 0 146 we must return the synthetic attribute "com.apple.FinderInfo". 147 Note: the client will never (never seen in traces) request that attribute 148 via FPGetExtAttr ! 149 */ 150 151 if (S_ISDIR(st->st_mode)) 152 adflags = ADFLAGS_DIR; 153 154 if ( ad_metadata( uname, adflags, adp) < 0 ) { 155 switch (errno) { 156 case ENOENT: 157 adp = NULL; 158 break; 159 case EACCES: 160 LOG(log_error, logtype_afpd, "afp_listextattr(%s): %s: check resource fork permission?", 161 uname, strerror(errno)); 162 return AFPERR_ACCESS; 163 default: 164 LOG(log_error, logtype_afpd, "afp_listextattr(%s): error getting metadata: %s", uname, strerror(errno)); 165 return AFPERR_MISC; 166 } 167 } 168 169 if (adp) { 170 FinderInfo = ad_entry(adp, ADEID_FINDERI); 171 172#ifdef DEBUG 173 LOG(log_maxdebug, logtype_afpd, "afp_listextattr(%s): FinderInfo:", uname); 174 hexdump( FinderInfo, 32); 175#endif 176 177 if ((adflags & ADFLAGS_DIR)) { 178 /* set default view */ 179 uint16 = htons(FINDERINFO_CLOSEDVIEW); 180 memcpy(emptyFinderInfo + FINDERINFO_FRVIEWOFF, &uint16, 2); 181 } 182 183 /* Check if FinderInfo equals default and empty FinderInfo*/ 184 if (memcmp(FinderInfo, emptyFinderInfo, 32) != 0) { 185 /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */ 186 strcpy(attrnamebuf, ea_finderinfo); 187 attrbuflen += strlen(ea_finderinfo) + 1; 188 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.FinderInfo", uname); 189 } 190 191 /* Now check for Ressource fork and add virtual EA "com.apple.ResourceFork" if size > 0 */ 192 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): Ressourcefork size: %llu", uname, adp->ad_rlen); 193 194 if (adp->ad_rlen > 0) { 195 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.RessourceFork.", uname); 196 strcpy(attrnamebuf + attrbuflen, ea_resourcefork); 197 attrbuflen += strlen(ea_resourcefork) + 1; 198 } 199 } 200 201 ret = vol->vfs->vfs_ea_list(vol, attrnamebuf, &attrbuflen, uname, oflag); 202 203 switch (ret) { 204 case AFPERR_BADTYPE: 205 /* its a symlink and client requested O_NOFOLLOW */ 206 LOG(log_debug, logtype_afpd, "afp_listextattr(%s): encountered symlink with kXAttrNoFollow", uname); 207 attrbuflen = 0; 208 buf_valid = 0; 209 ret = AFP_OK; 210 goto exit; 211 case AFPERR_MISC: 212 attrbuflen = 0; 213 goto exit; 214 default: 215 buf_valid = 1; 216 } 217 } 218 219 /* Start building reply packet */ 220 bitmap = htons(bitmap); 221 memcpy( rbuf, &bitmap, sizeof(bitmap)); 222 rbuf += sizeof(bitmap); 223 *rbuflen += sizeof(bitmap); 224 225 tmpattr = htonl(attrbuflen); 226 memcpy( rbuf, &tmpattr, sizeof(tmpattr)); 227 rbuf += sizeof(tmpattr); 228 *rbuflen += sizeof(tmpattr); 229 230 /* Only copy buffer if the client asked for it (2nd request, maxreply>0) 231 and we didnt have an error (buf_valid) */ 232 if (maxreply && buf_valid) { 233 memcpy( rbuf, attrnamebuf, attrbuflen); 234 *rbuflen += attrbuflen; 235 buf_valid = 0; 236 } 237 238 ret = AFP_OK; 239 240exit: 241 if (ret != AFP_OK) 242 buf_valid = 0; 243 if (adp) 244 ad_close_metadata( adp); 245 246 return ret; 247} 248 249static char *to_stringz(char *ibuf, uint16_t len) 250{ 251static char attrmname[256]; 252 253 if (len > 255) 254 /* dont fool with us */ 255 len = 255; 256 257 /* we must copy the name as its not 0-terminated and I DONT WANT TO WRITE to ibuf */ 258 strlcpy(attrmname, ibuf, len + 1); 259 return attrmname; 260} 261 262int afp_getextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen) 263{ 264 int ret, oflag = 0; 265 uint16_t vid, bitmap, attrnamelen; 266 uint32_t did, maxreply; 267 char attruname[256]; 268 struct vol *vol; 269 struct dir *dir; 270 struct path *s_path; 271 272 273 *rbuflen = 0; 274 ibuf += 2; 275 276 memcpy( &vid, ibuf, sizeof(vid)); 277 ibuf += sizeof(vid); 278 if (NULL == ( vol = getvolbyvid( vid )) ) { 279 LOG(log_debug, logtype_afpd, "afp_getextattr: getvolbyvid error: %s", strerror(errno)); 280 return AFPERR_ACCESS; 281 } 282 283 memcpy( &did, ibuf, sizeof(did)); 284 ibuf += sizeof(did); 285 if (NULL == ( dir = dirlookup( vol, did )) ) { 286 LOG(log_debug, logtype_afpd, "afp_getextattr: dirlookup error: %s", strerror(errno)); 287 return afp_errno; 288 } 289 290 memcpy( &bitmap, ibuf, sizeof(bitmap)); 291 bitmap = ntohs( bitmap ); 292 ibuf += sizeof(bitmap); 293 294 if (bitmap & kXAttrNoFollow) 295 oflag = O_NOFOLLOW; 296 297 /* Skip Offset and ReqCount */ 298 ibuf += 16; 299 300 /* Get MaxReply */ 301 memcpy(&maxreply, ibuf, sizeof(maxreply)); 302 maxreply = ntohl(maxreply); 303 ibuf += sizeof(maxreply); 304 305 /* get name */ 306 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) { 307 LOG(log_debug, logtype_afpd, "afp_getextattr: cname error: %s", strerror(errno)); 308 return AFPERR_NOOBJ; 309 } 310 311 if ((unsigned long)ibuf & 1) 312 ibuf++; 313 314 /* get length of EA name */ 315 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen)); 316 attrnamelen = ntohs(attrnamelen); 317 ibuf += sizeof(attrnamelen); 318 319 LOG(log_debug, logtype_afpd, "afp_getextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen)); 320 321 /* Convert EA name in utf8 to unix charset */ 322 if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, ibuf, attrnamelen, attruname, 256) ) 323 return AFPERR_MISC; 324 325 /* write bitmap now */ 326 bitmap = htons(bitmap); 327 memcpy(rbuf, &bitmap, sizeof(bitmap)); 328 rbuf += sizeof(bitmap); 329 *rbuflen += sizeof(bitmap); 330 331 /* 332 Switch on maxreply: 333 if its 0 we must return the size of the requested attribute, 334 if its non 0 we must return the attribute. 335 */ 336 if (maxreply == 0) 337 ret = vol->vfs->vfs_ea_getsize(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname); 338 else 339 ret = vol->vfs->vfs_ea_getcontent(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply); 340 341 return ret; 342} 343 344int afp_setextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen) 345{ 346 int oflag = 0, ret; 347 uint16_t vid, bitmap, attrnamelen; 348 uint32_t did, attrsize; 349 char attruname[256]; 350 char *attrmname; 351 struct vol *vol; 352 struct dir *dir; 353 struct path *s_path; 354 355 *rbuflen = 0; 356 ibuf += 2; 357 358 memcpy( &vid, ibuf, sizeof(vid)); 359 ibuf += sizeof(vid); 360 if (NULL == ( vol = getvolbyvid( vid )) ) { 361 LOG(log_debug, logtype_afpd, "afp_setextattr: getvolbyvid error: %s", strerror(errno)); 362 return AFPERR_ACCESS; 363 } 364 365 memcpy( &did, ibuf, sizeof(did)); 366 ibuf += sizeof(did); 367 if (NULL == ( dir = dirlookup( vol, did )) ) { 368 LOG(log_debug, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno)); 369 return afp_errno; 370 } 371 372 memcpy( &bitmap, ibuf, sizeof(bitmap)); 373 bitmap = ntohs( bitmap ); 374 ibuf += sizeof(bitmap); 375 376 if (bitmap & kXAttrNoFollow) 377 oflag |= O_NOFOLLOW; 378 379 if (bitmap & kXAttrCreate) 380 oflag |= O_CREAT; 381 else if (bitmap & kXAttrReplace) 382 oflag |= O_TRUNC; 383 384 /* Skip Offset */ 385 ibuf += 8; 386 387 /* get name */ 388 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) { 389 LOG(log_debug, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno)); 390 return AFPERR_NOOBJ; 391 } 392 393 if ((unsigned long)ibuf & 1) 394 ibuf++; 395 396 /* get length of EA name */ 397 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen)); 398 attrnamelen = ntohs(attrnamelen); 399 ibuf += sizeof(attrnamelen); 400 if (attrnamelen > 255) 401 return AFPERR_PARAM; 402 403 attrmname = ibuf; 404 /* Convert EA name in utf8 to unix charset */ 405 if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, attrmname, attrnamelen, attruname, 256)) 406 return AFPERR_MISC; 407 408 ibuf += attrnamelen; 409 /* get EA size */ 410 memcpy(&attrsize, ibuf, sizeof(attrsize)); 411 attrsize = ntohl(attrsize); 412 ibuf += sizeof(attrsize); 413 if (attrsize > MAX_EA_SIZE) 414 /* we arbitrarily make this fatal */ 415 return AFPERR_PARAM; 416 417 LOG(log_debug, logtype_afpd, "afp_setextattr(%s): EA: %s, size: %u", s_path->u_name, to_stringz(attrmname, attrnamelen), attrsize); 418 419 ret = vol->vfs->vfs_ea_set(vol, s_path->u_name, attruname, ibuf, attrsize, oflag); 420 421 return ret; 422} 423 424int afp_remextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen) 425{ 426 int oflag = 0, ret; 427 uint16_t vid, bitmap, attrnamelen; 428 uint32_t did; 429 char attruname[256]; 430 struct vol *vol; 431 struct dir *dir; 432 struct path *s_path; 433 434 *rbuflen = 0; 435 ibuf += 2; 436 437 memcpy( &vid, ibuf, sizeof(vid)); 438 ibuf += sizeof(vid); 439 if (NULL == ( vol = getvolbyvid( vid )) ) { 440 LOG(log_debug, logtype_afpd, "afp_remextattr: getvolbyvid error: %s", strerror(errno)); 441 return AFPERR_ACCESS; 442 } 443 444 memcpy( &did, ibuf, sizeof(did)); 445 ibuf += sizeof(did); 446 if (NULL == ( dir = dirlookup( vol, did )) ) { 447 LOG(log_debug, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno)); 448 return afp_errno; 449 } 450 451 memcpy( &bitmap, ibuf, sizeof(bitmap)); 452 bitmap = ntohs( bitmap ); 453 ibuf += sizeof(bitmap); 454 455 if (bitmap & kXAttrNoFollow) 456 oflag |= O_NOFOLLOW; 457 458 /* get name */ 459 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) { 460 LOG(log_debug, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno)); 461 return AFPERR_NOOBJ; 462 } 463 464 if ((unsigned long)ibuf & 1) 465 ibuf++; 466 467 /* get length of EA name */ 468 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen)); 469 attrnamelen = ntohs(attrnamelen); 470 ibuf += sizeof(attrnamelen); 471 if (attrnamelen > 255) 472 return AFPERR_PARAM; 473 474 /* Convert EA name in utf8 to unix charset */ 475 if ( 0 >= (convert_string(CH_UTF8_MAC, obj->options.unixcharset,ibuf, attrnamelen, attruname, 256)) ) 476 return AFPERR_MISC; 477 478 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen)); 479 480 ret = vol->vfs->vfs_ea_remove(vol, s_path->u_name, attruname, oflag); 481 482 return ret; 483} 484 485