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 /* Check if FinderInfo equals default and empty FinderInfo*/ 172 if (memcmp(FinderInfo, emptyFinderInfo, 32) != 0) { 173 /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */ 174 strcpy(attrnamebuf, ea_finderinfo); 175 attrbuflen += strlen(ea_finderinfo) + 1; 176 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.FinderInfo", uname); 177 } 178 179 /* Now check for Ressource fork and add virtual EA "com.apple.ResourceFork" if size > 0 */ 180 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): Ressourcefork size: %llu", uname, adp->ad_rlen); 181 182 if (adp->ad_rlen > 0) { 183 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.RessourceFork.", uname); 184 strcpy(attrnamebuf + attrbuflen, ea_resourcefork); 185 attrbuflen += strlen(ea_resourcefork) + 1; 186 } 187 } 188 189 ret = vol->vfs->vfs_ea_list(vol, attrnamebuf, &attrbuflen, uname, oflag); 190 191 switch (ret) { 192 case AFPERR_BADTYPE: 193 /* its a symlink and client requested O_NOFOLLOW */ 194 LOG(log_debug, logtype_afpd, "afp_listextattr(%s): encountered symlink with kXAttrNoFollow", uname); 195 attrbuflen = 0; 196 buf_valid = 0; 197 ret = AFP_OK; 198 goto exit; 199 case AFPERR_MISC: 200 attrbuflen = 0; 201 goto exit; 202 default: 203 buf_valid = 1; 204 } 205 } 206 207 /* Start building reply packet */ 208 bitmap = htons(bitmap); 209 memcpy( rbuf, &bitmap, sizeof(bitmap)); 210 rbuf += sizeof(bitmap); 211 *rbuflen += sizeof(bitmap); 212 213 tmpattr = htonl(attrbuflen); 214 memcpy( rbuf, &tmpattr, sizeof(tmpattr)); 215 rbuf += sizeof(tmpattr); 216 *rbuflen += sizeof(tmpattr); 217 218 /* Only copy buffer if the client asked for it (2nd request, maxreply>0) 219 and we didnt have an error (buf_valid) */ 220 if (maxreply && buf_valid) { 221 memcpy( rbuf, attrnamebuf, attrbuflen); 222 *rbuflen += attrbuflen; 223 buf_valid = 0; 224 } 225 226 ret = AFP_OK; 227 228exit: 229 if (ret != AFP_OK) 230 buf_valid = 0; 231 if (adp) 232 ad_close_metadata( adp); 233 234 return ret; 235} 236 237static char *to_stringz(char *ibuf, uint16_t len) 238{ 239static char attrmname[256]; 240 241 if (len > 255) 242 /* dont fool with us */ 243 len = 255; 244 245 /* we must copy the name as its not 0-terminated and I DONT WANT TO WRITE to ibuf */ 246 strlcpy(attrmname, ibuf, len + 1); 247 return attrmname; 248} 249 250int afp_getextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen) 251{ 252 int ret, oflag = 0; 253 uint16_t vid, bitmap, attrnamelen; 254 uint32_t did, maxreply; 255 char attruname[256]; 256 struct vol *vol; 257 struct dir *dir; 258 struct path *s_path; 259 260 261 *rbuflen = 0; 262 ibuf += 2; 263 264 memcpy( &vid, ibuf, sizeof(vid)); 265 ibuf += sizeof(vid); 266 if (NULL == ( vol = getvolbyvid( vid )) ) { 267 LOG(log_debug, logtype_afpd, "afp_getextattr: getvolbyvid error: %s", strerror(errno)); 268 return AFPERR_ACCESS; 269 } 270 271 memcpy( &did, ibuf, sizeof(did)); 272 ibuf += sizeof(did); 273 if (NULL == ( dir = dirlookup( vol, did )) ) { 274 LOG(log_debug, logtype_afpd, "afp_getextattr: dirlookup error: %s", strerror(errno)); 275 return afp_errno; 276 } 277 278 memcpy( &bitmap, ibuf, sizeof(bitmap)); 279 bitmap = ntohs( bitmap ); 280 ibuf += sizeof(bitmap); 281 282 if (bitmap & kXAttrNoFollow) 283 oflag = O_NOFOLLOW; 284 285 /* Skip Offset and ReqCount */ 286 ibuf += 16; 287 288 /* Get MaxReply */ 289 memcpy(&maxreply, ibuf, sizeof(maxreply)); 290 maxreply = ntohl(maxreply); 291 ibuf += sizeof(maxreply); 292 293 /* get name */ 294 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) { 295 LOG(log_debug, logtype_afpd, "afp_getextattr: cname error: %s", strerror(errno)); 296 return AFPERR_NOOBJ; 297 } 298 299 if ((unsigned long)ibuf & 1) 300 ibuf++; 301 302 /* get length of EA name */ 303 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen)); 304 attrnamelen = ntohs(attrnamelen); 305 ibuf += sizeof(attrnamelen); 306 307 LOG(log_debug, logtype_afpd, "afp_getextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen)); 308 309 /* Convert EA name in utf8 to unix charset */ 310 if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, ibuf, attrnamelen, attruname, 256) ) 311 return AFPERR_MISC; 312 313 /* write bitmap now */ 314 bitmap = htons(bitmap); 315 memcpy(rbuf, &bitmap, sizeof(bitmap)); 316 rbuf += sizeof(bitmap); 317 *rbuflen += sizeof(bitmap); 318 319 /* 320 Switch on maxreply: 321 if its 0 we must return the size of the requested attribute, 322 if its non 0 we must return the attribute. 323 */ 324 if (maxreply == 0) 325 ret = vol->vfs->vfs_ea_getsize(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname); 326 else 327 ret = vol->vfs->vfs_ea_getcontent(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply); 328 329 return ret; 330} 331 332int afp_setextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen) 333{ 334 int oflag = 0, ret; 335 uint16_t vid, bitmap, attrnamelen; 336 uint32_t did, attrsize; 337 char attruname[256]; 338 char *attrmname; 339 struct vol *vol; 340 struct dir *dir; 341 struct path *s_path; 342 343 *rbuflen = 0; 344 ibuf += 2; 345 346 memcpy( &vid, ibuf, sizeof(vid)); 347 ibuf += sizeof(vid); 348 if (NULL == ( vol = getvolbyvid( vid )) ) { 349 LOG(log_debug, logtype_afpd, "afp_setextattr: getvolbyvid error: %s", strerror(errno)); 350 return AFPERR_ACCESS; 351 } 352 353 memcpy( &did, ibuf, sizeof(did)); 354 ibuf += sizeof(did); 355 if (NULL == ( dir = dirlookup( vol, did )) ) { 356 LOG(log_debug, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno)); 357 return afp_errno; 358 } 359 360 memcpy( &bitmap, ibuf, sizeof(bitmap)); 361 bitmap = ntohs( bitmap ); 362 ibuf += sizeof(bitmap); 363 364 if (bitmap & kXAttrNoFollow) 365 oflag |= O_NOFOLLOW; 366 367 if (bitmap & kXAttrCreate) 368 oflag |= O_CREAT; 369 else if (bitmap & kXAttrReplace) 370 oflag |= O_TRUNC; 371 372 /* Skip Offset */ 373 ibuf += 8; 374 375 /* get name */ 376 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) { 377 LOG(log_debug, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno)); 378 return AFPERR_NOOBJ; 379 } 380 381 if ((unsigned long)ibuf & 1) 382 ibuf++; 383 384 /* get length of EA name */ 385 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen)); 386 attrnamelen = ntohs(attrnamelen); 387 ibuf += sizeof(attrnamelen); 388 if (attrnamelen > 255) 389 return AFPERR_PARAM; 390 391 attrmname = ibuf; 392 /* Convert EA name in utf8 to unix charset */ 393 if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, attrmname, attrnamelen, attruname, 256)) 394 return AFPERR_MISC; 395 396 ibuf += attrnamelen; 397 /* get EA size */ 398 memcpy(&attrsize, ibuf, sizeof(attrsize)); 399 attrsize = ntohl(attrsize); 400 ibuf += sizeof(attrsize); 401 if (attrsize > MAX_EA_SIZE) 402 /* we arbitrarily make this fatal */ 403 return AFPERR_PARAM; 404 405 LOG(log_debug, logtype_afpd, "afp_setextattr(%s): EA: %s, size: %u", s_path->u_name, to_stringz(attrmname, attrnamelen), attrsize); 406 407 ret = vol->vfs->vfs_ea_set(vol, s_path->u_name, attruname, ibuf, attrsize, oflag); 408 409 return ret; 410} 411 412int afp_remextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen) 413{ 414 int oflag = 0, ret; 415 uint16_t vid, bitmap, attrnamelen; 416 uint32_t did; 417 char attruname[256]; 418 struct vol *vol; 419 struct dir *dir; 420 struct path *s_path; 421 422 *rbuflen = 0; 423 ibuf += 2; 424 425 memcpy( &vid, ibuf, sizeof(vid)); 426 ibuf += sizeof(vid); 427 if (NULL == ( vol = getvolbyvid( vid )) ) { 428 LOG(log_debug, logtype_afpd, "afp_remextattr: getvolbyvid error: %s", strerror(errno)); 429 return AFPERR_ACCESS; 430 } 431 432 memcpy( &did, ibuf, sizeof(did)); 433 ibuf += sizeof(did); 434 if (NULL == ( dir = dirlookup( vol, did )) ) { 435 LOG(log_debug, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno)); 436 return afp_errno; 437 } 438 439 memcpy( &bitmap, ibuf, sizeof(bitmap)); 440 bitmap = ntohs( bitmap ); 441 ibuf += sizeof(bitmap); 442 443 if (bitmap & kXAttrNoFollow) 444 oflag |= O_NOFOLLOW; 445 446 /* get name */ 447 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) { 448 LOG(log_debug, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno)); 449 return AFPERR_NOOBJ; 450 } 451 452 if ((unsigned long)ibuf & 1) 453 ibuf++; 454 455 /* get length of EA name */ 456 memcpy(&attrnamelen, ibuf, sizeof(attrnamelen)); 457 attrnamelen = ntohs(attrnamelen); 458 ibuf += sizeof(attrnamelen); 459 if (attrnamelen > 255) 460 return AFPERR_PARAM; 461 462 /* Convert EA name in utf8 to unix charset */ 463 if ( 0 >= (convert_string(CH_UTF8_MAC, obj->options.unixcharset,ibuf, attrnamelen, attruname, 256)) ) 464 return AFPERR_MISC; 465 466 LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen)); 467 468 ret = vol->vfs->vfs_ea_remove(vol, s_path->u_name, attruname, oflag); 469 470 return ret; 471} 472 473