1/* 2 * Netatalk 2002 (c) 3 * Copyright (C) 1990, 1993 Regents of The University of Michigan 4 * Copyright (C) 2010 Frank Lahm 5 * All Rights Reserved. See COPYRIGHT 6 */ 7 8 9/* 10 * This file contains FPCatSearch implementation. FPCatSearch performs 11 * file/directory search based on specified criteria. It is used by client 12 * to perform fast searches on (propably) big volumes. So, it has to be 13 * pretty fast. 14 * 15 * This implementation bypasses most of adouble/libatalk stuff as long as 16 * possible and does a standard filesystem search. It calls higher-level 17 * libatalk/afpd functions only when it is really needed, mainly while 18 * returning some non-UNIX information or filtering by non-UNIX criteria. 19 * 20 * Initial version written by Rafal Lewczuk <rlewczuk@pronet.pl> 21 * 22 * Starting with Netatalk 2.2 searching by name criteria utilizes the 23 * CNID database in conjunction with an enhanced cnid_dbd. This requires 24 * the use of cnidscheme:dbd for the searched volume, the new functionality 25 * is not built into cnidscheme:cdb. 26 */ 27 28#ifdef HAVE_CONFIG_H 29#include "config.h" 30#endif /* HAVE_CONFIG_H */ 31 32#include <stdio.h> 33#include <stdlib.h> 34#include <errno.h> 35#include <ctype.h> 36#include <string.h> 37#include <time.h> 38#include <string.h> 39#include <sys/file.h> 40#include <netinet/in.h> 41 42#include <atalk/afp.h> 43#include <atalk/adouble.h> 44#include <atalk/logger.h> 45#include <atalk/cnid.h> 46#include <atalk/cnid_dbd_private.h> 47#include <atalk/util.h> 48#include <atalk/bstradd.h> 49#include <atalk/unicode.h> 50#include <atalk/globals.h> 51 52#include "desktop.h" 53#include "directory.h" 54#include "dircache.h" 55#include "file.h" 56#include "volume.h" 57#include "filedir.h" 58#include "fork.h" 59 60 61struct finderinfo { 62 u_int32_t f_type; 63 u_int32_t creator; 64 u_int16_t attrs; /* File attributes (high 8 bits)*/ 65 u_int16_t label; /* Label (low 8 bits )*/ 66 char reserved[22]; /* Unknown (at least for now...) */ 67}; 68 69typedef char packed_finder[ADEDLEN_FINDERI]; 70 71/* Known attributes: 72 * 0x04 - has a custom icon 73 * 0x20 - name/icon is locked 74 * 0x40 - is invisible 75 * 0x80 - is alias 76 * 77 * Known labels: 78 * 0x02 - project 2 79 * 0x04 - project 1 80 * 0x06 - personal 81 * 0x08 - cool 82 * 0x0a - in progress 83 * 0x0c - hot 84 * 0x0e - essential 85 */ 86 87/* This is our search-criteria structure. */ 88struct scrit { 89 u_int32_t rbitmap; /* Request bitmap - which values should we check ? */ 90 u_int16_t fbitmap, dbitmap; /* file & directory bitmap - which values should we return ? */ 91 u_int16_t attr; /* File attributes */ 92 time_t cdate; /* Creation date */ 93 time_t mdate; /* Last modification date */ 94 time_t bdate; /* Last backup date */ 95 u_int32_t pdid; /* Parent DID */ 96 u_int16_t offcnt; /* Offspring count */ 97 struct finderinfo finfo; /* Finder info */ 98 char lname[64]; /* Long name */ 99 char utf8name[514]; /* UTF8 or UCS2 name */ /* for convert_charset dest_len parameter +2 */ 100}; 101 102/* 103 * Directory tree search is recursive by its nature. But AFP specification 104 * requires FPCatSearch to pause after returning n results and be able to 105 * resume the search later. So we have to do recursive search using flat 106 * (iterative) algorithm and remember all directories to look into in an 107 * stack-like structure. The structure below is one item of directory stack. 108 * 109 */ 110struct dsitem { 111// struct dir *dir; /* Structure describing this directory */ 112// cnid_t did; /* CNID of this directory */ 113 int pidx; /* Parent's dsitem structure index. */ 114 int checked; /* Have we checked this directory ? */ 115 int path_len; 116 char *path; /* absolute UNIX path to this directory */ 117}; 118 119 120#define DS_BSIZE 128 121static int save_cidx = -1; /* Saved index of currently scanned directory. */ 122 123static struct dsitem *dstack = NULL; /* Directory stack data... */ 124static int dssize = 0; /* Directory stack (allocated) size... */ 125static int dsidx = 0; /* First free item index... */ 126 127static struct scrit c1, c2; /* search criteria */ 128 129/* Puts new item onto directory stack. */ 130static int addstack(char *uname, struct dir *dir, int pidx) 131{ 132 struct dsitem *ds; 133 size_t l, u; 134 135 /* check if we have some space on stack... */ 136 if (dsidx >= dssize) { 137 dssize += DS_BSIZE; 138 dstack = realloc(dstack, dssize * sizeof(struct dsitem)); 139 if (dstack == NULL) 140 return -1; 141 } 142 143 /* Put new element. Allocate and copy lname and path. */ 144 ds = dstack + dsidx++; 145// ds->did = dir->d_did; 146 ds->pidx = pidx; 147 ds->checked = 0; 148 if (pidx >= 0) { 149 l = dstack[pidx].path_len; 150 u = strlen(uname) +1; 151 if (!(ds->path = malloc(l + u + 1) )) 152 return -1; 153 memcpy(ds->path, dstack[pidx].path, l); 154 ds->path[l] = '/'; 155 memcpy(&ds->path[l+1], uname, u); 156 ds->path_len = l +u; 157 } 158 else { 159 ds->path = strdup(uname); 160 ds->path_len = strlen(uname); 161 } 162 return 0; 163} 164 165/* Removes checked items from top of directory stack. Returns index of the first unchecked elements or -1. */ 166static int reducestack(void) 167{ 168 int r; 169 if (save_cidx != -1) { 170 r = save_cidx; 171 save_cidx = -1; 172 return r; 173 } 174 175 while (dsidx > 0) { 176 if (dstack[dsidx-1].checked) { 177 dsidx--; 178 free(dstack[dsidx].path); 179 } else 180 return dsidx - 1; 181 } 182 return -1; 183} 184 185/* Clears directory stack. */ 186static void clearstack(void) 187{ 188 save_cidx = -1; 189 while (dsidx > 0) { 190 dsidx--; 191 free(dstack[dsidx].path); 192 } 193} 194 195/* Looks up for an opened adouble structure, opens resource fork of selected file. 196 * FIXME What about noadouble? 197*/ 198static struct adouble *adl_lkup(struct vol *vol, struct path *path, struct adouble *adp) 199{ 200 static struct adouble ad; 201 202 struct ofork *of; 203 int isdir; 204 205 if (adp) 206 return adp; 207 208 isdir = S_ISDIR(path->st.st_mode); 209 210 if (!isdir && (of = of_findname(path))) { 211 adp = of->of_ad; 212 } else { 213 ad_init(&ad, vol->v_adouble, vol->v_ad_options); 214 adp = &ad; 215 } 216 217 if ( ad_metadata( path->u_name, ((isdir) ? ADFLAGS_DIR : 0), adp) < 0 ) { 218 adp = NULL; /* FIXME without resource fork adl_lkup will be call again */ 219 } 220 221 return adp; 222} 223 224/* -------------------- */ 225static struct finderinfo *unpack_buffer(struct finderinfo *finfo, char *buffer) 226{ 227 memcpy(&finfo->f_type, buffer +FINDERINFO_FRTYPEOFF, sizeof(finfo->f_type)); 228 memcpy(&finfo->creator, buffer +FINDERINFO_FRCREATOFF, sizeof(finfo->creator)); 229 memcpy(&finfo->attrs, buffer +FINDERINFO_FRFLAGOFF, sizeof(finfo->attrs)); 230 memcpy(&finfo->label, buffer +FINDERINFO_FRFLAGOFF, sizeof(finfo->label)); 231 finfo->attrs &= 0xff00; /* high 8 bits */ 232 finfo->label &= 0xff; /* low 8 bits */ 233 234 return finfo; 235} 236 237/* -------------------- */ 238static struct finderinfo * 239unpack_finderinfo(struct vol *vol, struct path *path, struct adouble **adp, struct finderinfo *finfo, int islnk) 240{ 241 packed_finder buf; 242 void *ptr; 243 244 *adp = adl_lkup(vol, path, *adp); 245 ptr = get_finderinfo(vol, path->u_name, *adp, &buf,islnk); 246 return unpack_buffer(finfo, ptr); 247} 248 249/* -------------------- */ 250#define CATPBIT_PARTIAL 31 251/* Criteria checker. This function returns a 2-bit value. */ 252/* bit 0 means if checked file meets given criteria. */ 253/* bit 1 means if it is a directory and we should descent into it. */ 254/* uname - UNIX name 255 * fname - our fname (translated to UNIX) 256 * cidx - index in directory stack 257 */ 258static int crit_check(struct vol *vol, struct path *path) { 259 int result = 0; 260 u_int16_t attr, flags = CONV_PRECOMPOSE; 261 struct finderinfo *finfo = NULL, finderinfo; 262 struct adouble *adp = NULL; 263 time_t c_date, b_date; 264 u_int32_t ac_date, ab_date; 265 static char convbuf[514]; /* for convert_charset dest_len parameter +2 */ 266 size_t len; 267 int islnk; 268 islnk=S_ISLNK(path->st.st_mode); 269 270 if (S_ISDIR(path->st.st_mode)) { 271 if (!c1.dbitmap) 272 return 0; 273 } 274 else { 275 if (!c1.fbitmap) 276 return 0; 277 278 /* compute the Mac name 279 * first try without id (it's slow to find it) 280 * An other option would be to call get_id in utompath but 281 * we need to pass parent dir 282 */ 283 if (!(path->m_name = utompath(vol, path->u_name, 0 , utf8_encoding()) )) { 284 /*retry with the right id */ 285 286 cnid_t id; 287 288 adp = adl_lkup(vol, path, adp); 289 id = get_id(vol, adp, &path->st, path->d_dir->d_did, path->u_name, strlen(path->u_name)); 290 if (!id) { 291 /* FIXME */ 292 return 0; 293 } 294 /* save the id for getfilparm */ 295 path->id = id; 296 if (!(path->m_name = utompath(vol, path->u_name, id , utf8_encoding()))) { 297 return 0; 298 } 299 } 300 } 301 302 /* Kind of optimization: 303 * -- first check things we've already have - filename 304 * -- last check things we get from ad_open() 305 * FIXME strmcp strstr (icase) 306 */ 307 308 /* Check for filename */ 309 if ((c1.rbitmap & (1<<DIRPBIT_LNAME))) { 310 if ( (size_t)(-1) == (len = convert_string(vol->v_maccharset, CH_UCS2, path->m_name, -1, convbuf, 512)) ) 311 goto crit_check_ret; 312 313 if ((c1.rbitmap & (1<<CATPBIT_PARTIAL))) { 314 if (strcasestr_w( (ucs2_t*) convbuf, (ucs2_t*) c1.lname) == NULL) 315 goto crit_check_ret; 316 } else 317 if (strcasecmp_w((ucs2_t*) convbuf, (ucs2_t*) c1.lname) != 0) 318 goto crit_check_ret; 319 } 320 321 if ((c1.rbitmap & (1<<FILPBIT_PDINFO))) { 322 if ( (size_t)(-1) == (len = convert_charset( CH_UTF8_MAC, CH_UCS2, CH_UTF8, path->m_name, strlen(path->m_name), convbuf, 512, &flags))) { 323 goto crit_check_ret; 324 } 325 326 if (c1.rbitmap & (1<<CATPBIT_PARTIAL)) { 327 if (strcasestr_w((ucs2_t *) convbuf, (ucs2_t*)c1.utf8name) == NULL) 328 goto crit_check_ret; 329 } else 330 if (strcasecmp_w((ucs2_t *)convbuf, (ucs2_t*)c1.utf8name) != 0) 331 goto crit_check_ret; 332 } 333 334 335 /* FIXME */ 336 if ((unsigned)c2.mdate > 0x7fffffff) 337 c2.mdate = 0x7fffffff; 338 if ((unsigned)c2.cdate > 0x7fffffff) 339 c2.cdate = 0x7fffffff; 340 if ((unsigned)c2.bdate > 0x7fffffff) 341 c2.bdate = 0x7fffffff; 342 343 /* Check for modification date */ 344 if ((c1.rbitmap & (1<<DIRPBIT_MDATE))) { 345 if (path->st.st_mtime < c1.mdate || path->st.st_mtime > c2.mdate) 346 goto crit_check_ret; 347 } 348 349 /* Check for creation date... */ 350 if ((c1.rbitmap & (1<<DIRPBIT_CDATE))) { 351 c_date = path->st.st_mtime; 352 adp = adl_lkup(vol, path, adp); 353 if (adp && ad_getdate(adp, AD_DATE_CREATE, &ac_date) >= 0) 354 c_date = AD_DATE_TO_UNIX(ac_date); 355 356 if (c_date < c1.cdate || c_date > c2.cdate) 357 goto crit_check_ret; 358 } 359 360 /* Check for backup date... */ 361 if ((c1.rbitmap & (1<<DIRPBIT_BDATE))) { 362 b_date = path->st.st_mtime; 363 adp = adl_lkup(vol, path, adp); 364 if (adp && ad_getdate(adp, AD_DATE_BACKUP, &ab_date) >= 0) 365 b_date = AD_DATE_TO_UNIX(ab_date); 366 367 if (b_date < c1.bdate || b_date > c2.bdate) 368 goto crit_check_ret; 369 } 370 371 /* Check attributes */ 372 if ((c1.rbitmap & (1<<DIRPBIT_ATTR)) && c2.attr != 0) { 373 if ((adp = adl_lkup(vol, path, adp))) { 374 ad_getattr(adp, &attr); 375 if ((attr & c2.attr) != c1.attr) 376 goto crit_check_ret; 377 } else 378 goto crit_check_ret; 379 } 380 381 /* Check file type ID */ 382 if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.f_type != 0) { 383 finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk); 384 if (finfo->f_type != c1.finfo.f_type) 385 goto crit_check_ret; 386 } 387 388 /* Check creator ID */ 389 if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.creator != 0) { 390 if (!finfo) { 391 finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk); 392 } 393 if (finfo->creator != c1.finfo.creator) 394 goto crit_check_ret; 395 } 396 397 /* Check finder info attributes */ 398 if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.attrs != 0) { 399 if (!finfo) { 400 finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk); 401 } 402 403 if ((finfo->attrs & c2.finfo.attrs) != c1.finfo.attrs) 404 goto crit_check_ret; 405 } 406 407 /* Check label */ 408 if ((c1.rbitmap & (1<<DIRPBIT_FINFO)) && c2.finfo.label != 0) { 409 if (!finfo) { 410 finfo = unpack_finderinfo(vol, path, &adp, &finderinfo,islnk); 411 } 412 if ((finfo->label & c2.finfo.label) != c1.finfo.label) 413 goto crit_check_ret; 414 } 415 /* FIXME: Attributes check ! */ 416 417 /* All criteria are met. */ 418 result |= 1; 419crit_check_ret: 420 if (adp != NULL) 421 ad_close_metadata(adp); 422 return result; 423} 424 425/* ------------------------------ */ 426static int rslt_add ( struct vol *vol, struct path *path, char **buf, int ext) 427{ 428 429 char *p = *buf; 430 int ret; 431 size_t tbuf =0; 432 u_int16_t resultsize; 433 int isdir = S_ISDIR(path->st.st_mode); 434 435 /* Skip resultsize */ 436 if (ext) { 437 p += sizeof(resultsize); 438 } 439 else { 440 p++; 441 } 442 *p++ = isdir ? FILDIRBIT_ISDIR : FILDIRBIT_ISFILE; /* IsDir ? */ 443 444 if (ext) { 445 *p++ = 0; /* Pad */ 446 } 447 448 if ( isdir ) { 449 ret = getdirparams(vol, c1.dbitmap, path, path->d_dir, p , &tbuf ); 450 } 451 else { 452 /* FIXME slow if we need the file ID, we already know it, done ? */ 453 ret = getfilparams ( vol, c1.fbitmap, path, path->d_dir, p, &tbuf); 454 } 455 456 if ( ret != AFP_OK ) 457 return 0; 458 459 /* Make sure entry length is even */ 460 if ((tbuf & 1)) { 461 *p++ = 0; 462 tbuf++; 463 } 464 465 if (ext) { 466 resultsize = htons(tbuf); 467 memcpy ( *buf, &resultsize, sizeof(resultsize) ); 468 *buf += tbuf + 4; 469 } 470 else { 471 **buf = tbuf; 472 *buf += tbuf + 2; 473 } 474 475 return 1; 476} 477 478#define VETO_STR \ 479 "./../.AppleDouble/.AppleDB/Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/.AppleDesktop/.Parent/" 480 481/*! 482 * This function performs a filesystem search 483 * 484 * Uses globals c1, c2, the search criteria 485 * 486 * @param vol (r) volume we are searching on ... 487 * @param dir (rw) directory we are starting from ... 488 * @param rmatches (r) maximum number of matches we can return 489 * @param pos (r) position we've stopped recently 490 * @param rbuf (w) output buffer 491 * @param nrecs (w) number of matches 492 * @param rsize (w) length of data written to output buffer 493 * @param ext (r) extended search flag 494 */ 495#define NUM_ROUNDS 200 496static int catsearch(struct vol *vol, 497 struct dir *dir, 498 int rmatches, 499 uint32_t *pos, 500 char *rbuf, 501 uint32_t *nrecs, 502 int *rsize, 503 int ext) 504{ 505 static u_int32_t cur_pos; /* Saved position index (ID) - used to remember "position" across FPCatSearch calls */ 506 static DIR *dirpos; /* UNIX structure describing currently opened directory. */ 507 struct dir *curdir; /* struct dir of current directory */ 508 int cidx, r; 509 struct dirent *entry; 510 int result = AFP_OK; 511 int ccr; 512 struct path path; 513 char *vpath = vol->v_path; 514 char *rrbuf = rbuf; 515 time_t start_time; 516 int num_rounds = NUM_ROUNDS; 517 int cwd = -1; 518 int error; 519 520 if (*pos != 0 && *pos != cur_pos) { 521 result = AFPERR_CATCHNG; 522 goto catsearch_end; 523 } 524 525 /* FIXME: Category "offspring count ! */ 526 527 528 /* We need to initialize all mandatory structures/variables and change working directory appropriate... */ 529 if (*pos == 0) { 530 clearstack(); 531 if (dirpos != NULL) { 532 closedir(dirpos); 533 dirpos = NULL; 534 } 535 536 if (addstack(vpath, dir, -1) == -1) { 537 result = AFPERR_MISC; 538 goto catsearch_end; 539 } 540 /* FIXME: Sometimes DID is given by client ! (correct this one above !) */ 541 } 542 543 /* Save current path */ 544 if ((cwd = open(".", O_RDONLY)) < 0) { 545 result = AFPERR_MISC; 546 goto catsearch_end; 547 } 548 549 /* So we are beginning... */ 550 start_time = time(NULL); 551 552 while ((cidx = reducestack()) != -1) { 553 error = lchdir(dstack[cidx].path); 554 555 if (!error && dirpos == NULL) 556 dirpos = opendir("."); 557 558 if (dirpos == NULL) 559 dirpos = opendir(dstack[cidx].path); 560 561 if (error || dirpos == NULL) { 562 switch (errno) { 563 case EACCES: 564 dstack[cidx].checked = 1; 565 continue; 566 case EMFILE: 567 case ENFILE: 568 case ENOENT: 569 result = AFPERR_NFILE; 570 break; 571 case ENOMEM: 572 case ENOTDIR: 573 default: 574 result = AFPERR_MISC; 575 } /* switch (errno) */ 576 goto catsearch_end; 577 } 578 579 if ((curdir = dirlookup_bypath(vol, dstack[cidx].path)) == NULL) { 580 result = AFPERR_MISC; 581 goto catsearch_end; 582 } 583 584 while ((entry = readdir(dirpos)) != NULL) { 585 (*pos)++; 586 587 if (!check_dirent(vol, entry->d_name)) 588 continue; 589 590 memset(&path, 0, sizeof(path)); 591 path.u_name = entry->d_name; 592 if (of_stat(&path) != 0) { 593 switch (errno) { 594 case EACCES: 595 case ELOOP: 596 case ENOENT: 597 continue; 598 case ENOTDIR: 599 case EFAULT: 600 case ENOMEM: 601 case ENAMETOOLONG: 602 default: 603 result = AFPERR_MISC; 604 goto catsearch_end; 605 } 606 } 607 if (S_ISDIR(path.st.st_mode)) { 608 /* here we can short cut 609 ie if in the same loop the parent dir wasn't in the cache 610 ALL dirsearch_byname will fail. 611 */ 612 int unlen = strlen(path.u_name); 613 path.d_dir = dircache_search_by_name(vol, 614 curdir, 615 path.u_name, 616 unlen); 617 if (path.d_dir == NULL) { 618 /* path.m_name is set by adddir */ 619 if ((path.d_dir = dir_add(vol, 620 curdir, 621 &path, 622 unlen)) == NULL) { 623 result = AFPERR_MISC; 624 goto catsearch_end; 625 } 626 } 627 path.m_name = cfrombstr(path.d_dir->d_m_name); 628 629 if (addstack(path.u_name, path.d_dir, cidx) == -1) { 630 result = AFPERR_MISC; 631 goto catsearch_end; 632 } 633 } else { 634 path.d_dir = curdir; 635 } 636 637 ccr = crit_check(vol, &path); 638 639 /* bit 0 means that criteria has been met */ 640 if ((ccr & 1)) { 641 r = rslt_add ( vol, &path, &rrbuf, ext); 642 643 if (r == 0) { 644 result = AFPERR_MISC; 645 goto catsearch_end; 646 } 647 *nrecs += r; 648 /* Number of matches limit */ 649 if (--rmatches == 0) 650 goto catsearch_pause; 651 /* Block size limit */ 652 if (rrbuf - rbuf >= 448) 653 goto catsearch_pause; 654 } 655 /* MacOS 9 doesn't like servers executing commands longer than few seconds */ 656 if (--num_rounds <= 0) { 657 if (start_time != time(NULL)) { 658 result=AFP_OK; 659 goto catsearch_pause; 660 } 661 num_rounds = NUM_ROUNDS; 662 } 663 } /* while ((entry=readdir(dirpos)) != NULL) */ 664 closedir(dirpos); 665 dirpos = NULL; 666 dstack[cidx].checked = 1; 667 } /* while (current_idx = reducestack()) != -1) */ 668 669 /* We have finished traversing our tree. Return EOF here. */ 670 result = AFPERR_EOF; 671 goto catsearch_end; 672 673catsearch_pause: 674 cur_pos = *pos; 675 save_cidx = cidx; 676 677catsearch_end: /* Exiting catsearch: error condition */ 678 *rsize = rrbuf - rbuf; 679 if (cwd != -1) { 680 if ((fchdir(cwd)) != 0) { 681 LOG(log_debug, logtype_afpd, "error chdiring back: %s", strerror(errno)); 682 } 683 close(cwd); 684 } 685 return result; 686} /* catsearch() */ 687 688/*! 689 * This function performs a CNID db search 690 * 691 * Uses globals c1, c2, the search criteria 692 * 693 * @param vol (r) volume we are searching on ... 694 * @param dir (rw) directory we are starting from ... 695 * @param uname (r) UNIX name of object to search 696 * @param rmatches (r) maximum number of matches we can return 697 * @param pos (r) position we've stopped recently 698 * @param rbuf (w) output buffer 699 * @param nrecs (w) number of matches 700 * @param rsize (w) length of data written to output buffer 701 * @param ext (r) extended search flag 702 */ 703static int catsearch_db(struct vol *vol, 704 struct dir *dir, 705 const char *uname, 706 int rmatches, 707 uint32_t *pos, 708 char *rbuf, 709 uint32_t *nrecs, 710 int *rsize, 711 int ext) 712{ 713 static char resbuf[DBD_MAX_SRCH_RSLTS * sizeof(cnid_t)]; 714 static uint32_t cur_pos; 715 static int num_matches; 716 int ccr ,r; 717 int result = AFP_OK; 718 struct path path; 719 char *rrbuf = rbuf; 720 char buffer[MAXPATHLEN +2]; 721 uint16_t flags = CONV_TOLOWER; 722 723 LOG(log_debug, logtype_afpd, "catsearch_db(req pos: %u): {pos: %u, name: %s}", 724 *pos, cur_pos, uname); 725 726 if (*pos != 0 && *pos != cur_pos) { 727 result = AFPERR_CATCHNG; 728 goto catsearch_end; 729 } 730 731 if (cur_pos == 0 || *pos == 0) { 732 if (convert_charset(vol->v_volcharset, 733 vol->v_volcharset, 734 vol->v_maccharset, 735 uname, 736 strlen(uname), 737 buffer, 738 MAXPATHLEN, 739 &flags) == (size_t)-1) { 740 LOG(log_error, logtype_afpd, "catsearch_db: conversion error"); 741 result = AFPERR_MISC; 742 goto catsearch_end; 743 } 744 745 LOG(log_debug, logtype_afpd, "catsearch_db: %s", buffer); 746 747 if ((num_matches = cnid_find(vol->v_cdb, 748 buffer, 749 strlen(uname), 750 resbuf, 751 sizeof(resbuf))) == -1) { 752 result = AFPERR_MISC; 753 goto catsearch_end; 754 } 755 } 756 757 while (cur_pos < num_matches) { 758 char *name; 759 cnid_t cnid, did; 760 char resolvebuf[12 + MAXPATHLEN + 1]; 761 struct dir *dir; 762 763 /* Next CNID to process from buffer */ 764 memcpy(&cnid, resbuf + cur_pos * sizeof(cnid_t), sizeof(cnid_t)); 765 did = cnid; 766 767 if ((name = cnid_resolve(vol->v_cdb, &did, resolvebuf, 12 + MAXPATHLEN + 1)) == NULL) 768 goto next; 769 LOG(log_debug, logtype_afpd, "catsearch_db: {pos: %u, name:%s, cnid: %u}", 770 cur_pos, name, ntohl(cnid)); 771 if ((dir = dirlookup(vol, did)) == NULL) 772 goto next; 773 if (movecwd(vol, dir) < 0 ) 774 goto next; 775 776 memset(&path, 0, sizeof(path)); 777 path.u_name = name; 778 path.m_name = utompath(vol, name, cnid, utf8_encoding()); 779 780 if (of_stat(&path) != 0) { 781 switch (errno) { 782 case EACCES: 783 case ELOOP: 784 goto next; 785 case ENOENT: 786 787 default: 788 result = AFPERR_MISC; 789 goto catsearch_end; 790 } 791 } 792 /* For files path.d_dir is the parent dir, for dirs its the dir itself */ 793 if (S_ISDIR(path.st.st_mode)) 794 if ((dir = dirlookup(vol, cnid)) == NULL) 795 goto next; 796 path.d_dir = dir; 797 798 LOG(log_maxdebug, logtype_afpd,"catsearch_db: dir: %s, cwd: %s, name: %s", 799 cfrombstr(dir->d_fullpath), getcwdpath(), path.u_name); 800 801 /* At last we can check the search criteria */ 802 ccr = crit_check(vol, &path); 803 if ((ccr & 1)) { 804 LOG(log_debug, logtype_afpd,"catsearch_db: match: %s/%s", 805 getcwdpath(), path.u_name); 806 /* bit 1 means that criteria has been met */ 807 r = rslt_add(vol, &path, &rrbuf, ext); 808 if (r == 0) { 809 result = AFPERR_MISC; 810 goto catsearch_end; 811 } 812 *nrecs += r; 813 /* Number of matches limit */ 814 if (--rmatches == 0) 815 goto catsearch_pause; 816 /* Block size limit */ 817 if (rrbuf - rbuf >= 448) 818 goto catsearch_pause; 819 } 820 next: 821 cur_pos++; 822 } /* while */ 823 824 /* finished */ 825 result = AFPERR_EOF; 826 cur_pos = 0; 827 goto catsearch_end; 828 829catsearch_pause: 830 *pos = cur_pos; 831 832catsearch_end: /* Exiting catsearch: error condition */ 833 *rsize = rrbuf - rbuf; 834 LOG(log_debug, logtype_afpd, "catsearch_db(req pos: %u): {pos: %u}", *pos, cur_pos); 835 return result; 836} 837 838/* -------------------------- */ 839static int catsearch_afp(AFPObj *obj _U_, char *ibuf, size_t ibuflen, 840 char *rbuf, size_t *rbuflen, int ext) 841{ 842 struct vol *vol; 843 u_int16_t vid; 844 u_int16_t spec_len; 845 u_int32_t rmatches, reserved; 846 u_int32_t catpos[4]; 847 u_int32_t pdid = 0; 848 int ret, rsize; 849 u_int32_t nrecs = 0; 850 unsigned char *spec1, *spec2, *bspec1, *bspec2; 851 size_t len; 852 u_int16_t namelen; 853 u_int16_t flags; 854 char tmppath[256]; 855 char *uname; 856 857 *rbuflen = 0; 858 859 /* min header size */ 860 if (ibuflen < 32) { 861 return AFPERR_PARAM; 862 } 863 864 memset(&c1, 0, sizeof(c1)); 865 memset(&c2, 0, sizeof(c2)); 866 867 ibuf += 2; 868 memcpy(&vid, ibuf, sizeof(vid)); 869 ibuf += sizeof(vid); 870 871 if ((vol = getvolbyvid(vid)) == NULL) { 872 return AFPERR_PARAM; 873 } 874 875 memcpy(&rmatches, ibuf, sizeof(rmatches)); 876 rmatches = ntohl(rmatches); 877 ibuf += sizeof(rmatches); 878 879 /* FIXME: (rl) should we check if reserved == 0 ? */ 880 ibuf += sizeof(reserved); 881 882 memcpy(catpos, ibuf, sizeof(catpos)); 883 ibuf += sizeof(catpos); 884 885 memcpy(&c1.fbitmap, ibuf, sizeof(c1.fbitmap)); 886 c1.fbitmap = c2.fbitmap = ntohs(c1.fbitmap); 887 ibuf += sizeof(c1.fbitmap); 888 889 memcpy(&c1.dbitmap, ibuf, sizeof(c1.dbitmap)); 890 c1.dbitmap = c2.dbitmap = ntohs(c1.dbitmap); 891 ibuf += sizeof(c1.dbitmap); 892 893 memcpy(&c1.rbitmap, ibuf, sizeof(c1.rbitmap)); 894 c1.rbitmap = c2.rbitmap = ntohl(c1.rbitmap); 895 ibuf += sizeof(c1.rbitmap); 896 897 if (! (c1.fbitmap || c1.dbitmap)) { 898 return AFPERR_BITMAP; 899 } 900 901 if ( ext) { 902 memcpy(&spec_len, ibuf, sizeof(spec_len)); 903 spec_len = ntohs(spec_len); 904 } 905 else { 906 /* with catsearch only name and parent id are allowed */ 907 c1.fbitmap &= (1<<FILPBIT_LNAME) | (1<<FILPBIT_PDID); 908 c1.dbitmap &= (1<<DIRPBIT_LNAME) | (1<<DIRPBIT_PDID); 909 spec_len = *(unsigned char*)ibuf; 910 } 911 912 /* Parse file specifications */ 913 spec1 = (unsigned char*)ibuf; 914 spec2 = (unsigned char*)ibuf + spec_len + 2; 915 916 spec1 += 2; 917 spec2 += 2; 918 919 bspec1 = spec1; 920 bspec2 = spec2; 921 /* File attribute bits... */ 922 if (c1.rbitmap & (1 << FILPBIT_ATTR)) { 923 memcpy(&c1.attr, spec1, sizeof(c1.attr)); 924 spec1 += sizeof(c1.attr); 925 memcpy(&c2.attr, spec2, sizeof(c2.attr)); 926 spec2 += sizeof(c1.attr); 927 } 928 929 /* Parent DID */ 930 if (c1.rbitmap & (1 << FILPBIT_PDID)) { 931 memcpy(&c1.pdid, spec1, sizeof(pdid)); 932 spec1 += sizeof(c1.pdid); 933 memcpy(&c2.pdid, spec2, sizeof(pdid)); 934 spec2 += sizeof(c2.pdid); 935 } /* FIXME: PDID - do we demarshall this argument ? */ 936 937 /* Creation date */ 938 if (c1.rbitmap & (1 << FILPBIT_CDATE)) { 939 memcpy(&c1.cdate, spec1, sizeof(c1.cdate)); 940 spec1 += sizeof(c1.cdate); 941 c1.cdate = AD_DATE_TO_UNIX(c1.cdate); 942 memcpy(&c2.cdate, spec2, sizeof(c2.cdate)); 943 spec2 += sizeof(c1.cdate); 944 ibuf += sizeof(c1.cdate);; 945 c2.cdate = AD_DATE_TO_UNIX(c2.cdate); 946 } 947 948 /* Modification date */ 949 if (c1.rbitmap & (1 << FILPBIT_MDATE)) { 950 memcpy(&c1.mdate, spec1, sizeof(c1.mdate)); 951 c1.mdate = AD_DATE_TO_UNIX(c1.mdate); 952 spec1 += sizeof(c1.mdate); 953 memcpy(&c2.mdate, spec2, sizeof(c2.mdate)); 954 c2.mdate = AD_DATE_TO_UNIX(c2.mdate); 955 spec2 += sizeof(c1.mdate); 956 } 957 958 /* Backup date */ 959 if (c1.rbitmap & (1 << FILPBIT_BDATE)) { 960 memcpy(&c1.bdate, spec1, sizeof(c1.bdate)); 961 spec1 += sizeof(c1.bdate); 962 c1.bdate = AD_DATE_TO_UNIX(c1.bdate); 963 memcpy(&c2.bdate, spec2, sizeof(c2.bdate)); 964 spec2 += sizeof(c2.bdate); 965 c1.bdate = AD_DATE_TO_UNIX(c2.bdate); 966 } 967 968 /* Finder info */ 969 if (c1.rbitmap & (1 << FILPBIT_FINFO)) { 970 packed_finder buf; 971 972 memcpy(buf, spec1, sizeof(buf)); 973 unpack_buffer(&c1.finfo, buf); 974 spec1 += sizeof(buf); 975 976 memcpy(buf, spec2, sizeof(buf)); 977 unpack_buffer(&c2.finfo, buf); 978 spec2 += sizeof(buf); 979 } /* Finder info */ 980 981 if ((c1.rbitmap & (1 << DIRPBIT_OFFCNT)) != 0) { 982 /* Offspring count - only directories */ 983 if (c1.fbitmap == 0) { 984 memcpy(&c1.offcnt, spec1, sizeof(c1.offcnt)); 985 spec1 += sizeof(c1.offcnt); 986 c1.offcnt = ntohs(c1.offcnt); 987 memcpy(&c2.offcnt, spec2, sizeof(c2.offcnt)); 988 spec2 += sizeof(c2.offcnt); 989 c2.offcnt = ntohs(c2.offcnt); 990 } 991 else if (c1.dbitmap == 0) { 992 /* ressource fork length */ 993 } 994 else { 995 return AFPERR_BITMAP; /* error */ 996 } 997 } /* Offspring count/ressource fork length */ 998 999 /* Long name */ 1000 if (c1.rbitmap & (1 << FILPBIT_LNAME)) { 1001 /* Get the long filename */ 1002 memcpy(tmppath, bspec1 + spec1[1] + 1, (bspec1 + spec1[1])[0]); 1003 tmppath[(bspec1 + spec1[1])[0]]= 0; 1004 len = convert_string ( vol->v_maccharset, CH_UCS2, tmppath, -1, c1.lname, sizeof(c1.lname)); 1005 if (len == (size_t)(-1)) 1006 return AFPERR_PARAM; 1007 1008#if 0 1009 /* FIXME: do we need it ? It's always null ! */ 1010 memcpy(c2.lname, bspec2 + spec2[1] + 1, (bspec2 + spec2[1])[0]); 1011 c2.lname[(bspec2 + spec2[1])[0]]= 0; 1012#endif 1013 } 1014 /* UTF8 Name */ 1015 if (c1.rbitmap & (1 << FILPBIT_PDINFO)) { 1016 1017 /* offset */ 1018 memcpy(&namelen, spec1, sizeof(namelen)); 1019 namelen = ntohs (namelen); 1020 1021 spec1 = bspec1+namelen+4; /* Skip Unicode Hint */ 1022 1023 /* length */ 1024 memcpy(&namelen, spec1, sizeof(namelen)); 1025 namelen = ntohs (namelen); 1026 if (namelen > UTF8FILELEN_EARLY) /* Safeguard */ 1027 namelen = UTF8FILELEN_EARLY; 1028 1029 memcpy (c1.utf8name, spec1+2, namelen); 1030 c1.utf8name[namelen] = 0; 1031 if ((uname = mtoupath(vol, c1.utf8name, 0, utf8_encoding())) == NULL) 1032 return AFPERR_PARAM; 1033 1034 /* convert charset */ 1035 flags = CONV_PRECOMPOSE; 1036 len = convert_charset(CH_UTF8_MAC, CH_UCS2, CH_UTF8, c1.utf8name, namelen, c1.utf8name, 512, &flags); 1037 if (len == (size_t)(-1)) 1038 return AFPERR_PARAM; 1039 } 1040 1041 /* Call search */ 1042 *rbuflen = 24; 1043 if ((c1.rbitmap & (1 << FILPBIT_PDINFO)) 1044 && (strcmp(vol->v_cnidscheme, "dbd") == 0) 1045 && (vol->v_flags & AFPVOL_SEARCHDB)) 1046 /* we've got a name and it's a dbd volume, so search CNID database */ 1047 ret = catsearch_db(vol, vol->v_root, uname, rmatches, &catpos[0], rbuf+24, &nrecs, &rsize, ext); 1048 else 1049 /* perform a slow filesystem tree search */ 1050 ret = catsearch(vol, vol->v_root, rmatches, &catpos[0], rbuf+24, &nrecs, &rsize, ext); 1051 1052 memcpy(rbuf, catpos, sizeof(catpos)); 1053 rbuf += sizeof(catpos); 1054 1055 c1.fbitmap = htons(c1.fbitmap); 1056 memcpy(rbuf, &c1.fbitmap, sizeof(c1.fbitmap)); 1057 rbuf += sizeof(c1.fbitmap); 1058 1059 c1.dbitmap = htons(c1.dbitmap); 1060 memcpy(rbuf, &c1.dbitmap, sizeof(c1.dbitmap)); 1061 rbuf += sizeof(c1.dbitmap); 1062 1063 nrecs = htonl(nrecs); 1064 memcpy(rbuf, &nrecs, sizeof(nrecs)); 1065 rbuf += sizeof(nrecs); 1066 *rbuflen += rsize; 1067 1068 return ret; 1069} /* catsearch_afp */ 1070 1071/* -------------------------- */ 1072int afp_catsearch (AFPObj *obj, char *ibuf, size_t ibuflen, 1073 char *rbuf, size_t *rbuflen) 1074{ 1075 return catsearch_afp( obj, ibuf, ibuflen, rbuf, rbuflen, 0); 1076} 1077 1078 1079int afp_catsearch_ext (AFPObj *obj, char *ibuf, size_t ibuflen, 1080 char *rbuf, size_t *rbuflen) 1081{ 1082 return catsearch_afp( obj, ibuf, ibuflen, rbuf, rbuflen, 1); 1083} 1084 1085/* FIXME: we need a clean separation between afp stubs and 'real' implementation */ 1086/* (so, all buffer packing/unpacking should be done in stub, everything else 1087 should be done in other functions) */ 1088