1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <fs/fs_subr.h> 27 28 #include <sys/elf.h> 29 #include <sys/errno.h> 30 #include <sys/file.h> 31 #include <sys/kmem.h> 32 #include <sys/kobj.h> 33 #include <sys/objfs.h> 34 #include <sys/objfs_impl.h> 35 #include <sys/stat.h> 36 #include <sys/systm.h> 37 #include <sys/sysmacros.h> 38 #include <sys/vfs_opreg.h> 39 40 /* 41 * /system/object/<obj>/object 42 * 43 * This is an ELF file that contains information about data stored in the 44 * kernel. We use a special ELF file type, ET_SUNWPSEUDO, so that we can 45 * control which fields and sections have meaning. The file contains the 46 * following sections: 47 * 48 * .shstrtab Section header string table 49 * .SUNW_ctf CTF data 50 * .symtab Symbol table 51 * .strtab String table 52 * .text Text 53 * .data Data 54 * .bss BSS 55 * .filename Filename of module 56 * .info Private module info structure 57 * 58 * The .text, .data, and .bss sections are all marked SHT_NOBITS, and the data 59 * is not actually exported in the file for security reasons. The section 60 * headers do contain the address and size of the sections, which is needed by 61 * DTrace. The CTF data, symbol table, and string table are present only if 62 * they exist in the kernel. 63 */ 64 65 typedef enum { 66 SECT_TYPE_DATA, 67 SECT_TYPE_SHSTRTAB, 68 SECT_TYPE_DUMMY, 69 SECT_TYPE_SYMTAB, 70 SECT_TYPE_STRTAB, 71 SECT_TYPE_FILENAME, 72 SECT_TYPE_INFO 73 } sect_type_t; 74 75 typedef struct section_desc { 76 sect_type_t sect_id; 77 const char *sect_name; 78 uintptr_t sect_addr; 79 size_t sect_size; 80 int sect_type; 81 int sect_flags; 82 size_t sect_str; 83 int sect_link; 84 int sect_entsize; 85 int sect_align; 86 } section_desc_t; 87 88 /* 89 * For data sections, 'addr' and 'size' refer to offsets within the module 90 * structure where we can find the address and size of the section. 91 */ 92 #define SECT_DATA(name, addr, size, type, flags, align) \ 93 { SECT_TYPE_DATA, name, offsetof(struct module, addr), \ 94 offsetof(struct module, size), type, flags, 0, 0, 0, align } 95 96 /* 97 * The dummy section is the initial section of the file. It is put into this 98 * array only for convenience when reading the file. 99 */ 100 #define SECT_DUMMY { SECT_TYPE_DUMMY, "", 0, 0, 0, 0, 0, 0, 0, 0 } 101 102 /* 103 * The size of the symbol table and string table are not immediately available 104 * as an offset into the module struct, so we have to create individual types 105 * for each. 106 */ 107 #ifdef _LP64 108 #define SECT_SYMTAB(name, type, flags) \ 109 { SECT_TYPE_SYMTAB, name, offsetof(struct module, symtbl), 0, type, \ 110 flags, 0, 0, sizeof (Elf64_Sym), sizeof (uint64_t) } 111 #else 112 #define SECT_SYMTAB(name, type, flags) \ 113 { SECT_TYPE_SYMTAB, name, offsetof(struct module, symtbl), 0, type, \ 114 flags, 0, 0, sizeof (Elf32_Sym), sizeof (uint32_t) } 115 #endif 116 #define SECT_STRTAB(name, type, flags) \ 117 { SECT_TYPE_STRTAB, name, offsetof(struct module, strings), 0, type, \ 118 flags, 0, 0, 0, 1 } 119 120 /* 121 * The .shstrtab section is constructed when the module is first loaded. 122 */ 123 #define SECT_SHSTRTAB(name, type, flags) \ 124 { SECT_TYPE_SHSTRTAB, name, 0, 0, type, flags, 0, 0, 0, 1 } 125 126 /* 127 * Generic module information (objfs_info_t) 128 */ 129 #define SECT_INFO \ 130 { SECT_TYPE_INFO, ".info", 0, 0, SHT_PROGBITS, 0, 0, 0, 0, \ 131 sizeof (uint32_t) } 132 133 /* 134 * Filename section. 135 */ 136 #define SECT_FILENAME \ 137 { SECT_TYPE_FILENAME, ".filename", 0, 0, SHT_PROGBITS, 0, 0, 0, 0, 1 } 138 139 static section_desc_t data_sections[] = { 140 SECT_DUMMY, 141 SECT_SHSTRTAB(".shstrtab", 142 SHT_STRTAB, SHF_STRINGS), 143 SECT_DATA(".SUNW_ctf", ctfdata, ctfsize, 144 SHT_PROGBITS, 0, sizeof (uint64_t)), 145 SECT_SYMTAB(".symtab", SHT_SYMTAB, 0), 146 SECT_STRTAB(".strtab", SHT_STRTAB, SHF_STRINGS), 147 SECT_DATA(".text", text, text_size, 148 SHT_NOBITS, SHF_ALLOC | SHF_EXECINSTR, 0), 149 SECT_DATA(".data", data, data_size, 150 SHT_NOBITS, SHF_WRITE | SHF_ALLOC, 0), 151 SECT_DATA(".bss", bss, bss_size, 152 SHT_NOBITS, SHF_WRITE | SHF_ALLOC, 0), 153 SECT_INFO, 154 SECT_FILENAME 155 }; 156 157 #define NSECTIONS \ 158 (sizeof (data_sections) / sizeof (section_desc_t)) 159 160 #ifdef _LP64 161 #define SECTION_OFFSET(section) \ 162 (sizeof (Elf64_Ehdr) + (section) * sizeof (Elf64_Shdr)) 163 #else 164 #define SECTION_OFFSET(section) \ 165 (sizeof (Elf32_Ehdr) + (section) * sizeof (Elf32_Shdr)) 166 #endif 167 168 /* 169 * Given a data node, returns the struct module appropriately locked. If the 170 * object has been unloaded, or re-loaded since the file was first opened, this 171 * function will return NULL. If successful, the caller must call 172 * objfs_data_unlock(). 173 */ 174 struct module * 175 objfs_data_lock(vnode_t *vp) 176 { 177 objfs_datanode_t *dnode = vp->v_data; 178 objfs_odirnode_t *odir = gfs_file_parent(vp)->v_data; 179 struct modctl *mp = odir->objfs_odir_modctl; 180 181 (void) mod_hold_by_modctl(mp, MOD_WAIT_FOREVER | MOD_LOCK_NOT_HELD); 182 183 if (mp->mod_mp == NULL || 184 dnode->objfs_data_gencount < mp->mod_gencount) { 185 mod_release_mod(mp); 186 return (NULL); 187 } 188 189 return (mp->mod_mp); 190 } 191 192 void 193 objfs_data_unlock(vnode_t *vp) 194 { 195 objfs_odirnode_t *odir = gfs_file_parent(vp)->v_data; 196 197 mod_release_mod(odir->objfs_odir_modctl); 198 } 199 200 201 /* 202 * Called when the filesystem is first loaded. Creates and initializes the 203 * section header string table, and fills in the sect_str members of the section 204 * descriptors. This information could be encoded at compile-time, but this 205 * way keeps the code more maintainable, as we don't have to worry about 206 * duplicating information. 207 */ 208 void 209 objfs_data_init(void) 210 { 211 int i, shstrtab, strtab, symtab; 212 size_t len = 0; 213 section_desc_t *sect; 214 char *strdata; 215 216 for (i = 0; i < NSECTIONS; i++) { 217 sect = &data_sections[i]; 218 219 ASSERT(sect->sect_align == 0 || ISP2(sect->sect_align)); 220 ASSERT(sect->sect_align <= sizeof (uint64_t)); 221 222 len += strlen(sect->sect_name) + 1; 223 if (strcmp(sect->sect_name, ".shstrtab") == 0) 224 shstrtab = i; 225 else if (strcmp(sect->sect_name, ".symtab") == 0) 226 symtab = i; 227 else if (strcmp(sect->sect_name, ".strtab") == 0) 228 strtab = i; 229 } 230 231 strdata = kmem_zalloc(len, KM_SLEEP); 232 sect = &data_sections[shstrtab]; 233 sect->sect_addr = (uintptr_t)strdata; 234 sect->sect_size = len; 235 236 len = 0; 237 for (i = 0; i < NSECTIONS; i++) { 238 sect = &data_sections[i]; 239 sect->sect_str = len; 240 bcopy(sect->sect_name, strdata + len, 241 strlen(sect->sect_name) + 1); 242 len += strlen(sect->sect_name) + 1; 243 244 if (strcmp(sect->sect_name, ".SUNW_ctf") == 0) 245 sect->sect_link = symtab; 246 else if (strcmp(sect->sect_name, ".symtab") == 0) 247 sect->sect_link = strtab; 248 } 249 } 250 251 /* 252 * Given a section descriptor and module pointer, return the address of the 253 * data. 254 */ 255 static uintptr_t 256 sect_addr(section_desc_t *sp, struct module *mp) 257 { 258 uintptr_t addr; 259 260 switch (sp->sect_id) { 261 case SECT_TYPE_DUMMY: 262 addr = 0; 263 break; 264 265 case SECT_TYPE_SHSTRTAB: 266 addr = sp->sect_addr; 267 break; 268 269 case SECT_TYPE_STRTAB: 270 case SECT_TYPE_SYMTAB: 271 case SECT_TYPE_DATA: 272 addr = *((uintptr_t *)((char *)mp + sp->sect_addr)); 273 break; 274 275 case SECT_TYPE_FILENAME: 276 addr = (uintptr_t)mp->filename; 277 break; 278 279 case SECT_TYPE_INFO: 280 addr = 1; /* This can be anything nonzero */ 281 break; 282 } 283 284 return (addr); 285 } 286 287 /* 288 * Given a section descriptor and module pointer, return the size of the data. 289 */ 290 static size_t 291 sect_size(section_desc_t *sp, struct module *mp) 292 { 293 size_t size; 294 295 switch (sp->sect_id) { 296 case SECT_TYPE_DUMMY: 297 size = 0; 298 break; 299 300 case SECT_TYPE_SHSTRTAB: 301 size = sp->sect_size; 302 break; 303 304 case SECT_TYPE_DATA: 305 size = *((size_t *)((char *)mp + sp->sect_size)); 306 break; 307 308 case SECT_TYPE_SYMTAB: 309 size = mp->symhdr->sh_size; 310 break; 311 312 case SECT_TYPE_STRTAB: 313 size = mp->strhdr->sh_size; 314 break; 315 316 case SECT_TYPE_INFO: 317 size = sizeof (objfs_info_t); 318 break; 319 320 case SECT_TYPE_FILENAME: 321 if (mp->filename == NULL) 322 size = 0; 323 else 324 size = strlen(mp->filename) + 1; 325 } 326 327 return (size); 328 } 329 330 /* 331 * Given a section descriptor and module pointer, return 1 if the section has 332 * valid data and should be included, 0 otherwise. 333 */ 334 static int 335 sect_valid(section_desc_t *sp, struct module *mp) 336 { 337 if (sp->sect_id == SECT_TYPE_DUMMY || 338 sect_addr(sp, mp) != 0) 339 return (1); 340 341 return (0); 342 } 343 344 /* 345 * Given a section descriptor and module pointer, return the offset into the 346 * file where the data should be placed. 347 */ 348 static size_t 349 data_offset(section_desc_t *sp, struct module *mp) 350 { 351 int i; 352 size_t len; 353 section_desc_t *cp; 354 355 if (sp != NULL && mp != NULL && !sect_valid(sp, mp)) 356 return (0); 357 358 #ifdef _LP64 359 len = sizeof (Elf64_Ehdr); 360 #else 361 len = sizeof (Elf32_Ehdr); 362 #endif 363 364 /* 365 * Do a first pass to account for all the section headers. 366 */ 367 for (i = 0; i < NSECTIONS; i++) { 368 if (sect_valid(&data_sections[i], mp)) { 369 #ifdef _LP64 370 len += sizeof (Elf64_Shdr); 371 #else 372 len += sizeof (Elf32_Shdr); 373 #endif 374 } 375 } 376 377 /* 378 * Add length of each section until we find the one we're looking for. 379 */ 380 for (i = 0; i < NSECTIONS; i++) { 381 cp = &data_sections[i]; 382 383 /* 384 * Align the section only if it's valid and contains data. When 385 * searching for a specific section, align the section before 386 * breaking out of the loop. 387 */ 388 if (sect_valid(cp, mp) && cp->sect_type != SHT_NOBITS) { 389 if (cp->sect_align > 1) 390 len = P2ROUNDUP(len, cp->sect_align); 391 392 if (sp != cp) 393 len += sect_size(cp, mp); 394 } 395 396 if (sp == cp) 397 break; 398 } 399 400 return (len); 401 } 402 403 /* 404 * Given an index into the section table and a module pointer, returns the 405 * data offset of the next section. 406 */ 407 static size_t 408 next_offset(int idx, struct module *mp) 409 { 410 int i; 411 412 for (i = idx + 1; i < NSECTIONS; i++) { 413 if (sect_valid(&data_sections[i], mp)) 414 return (data_offset(&data_sections[i], mp)); 415 } 416 417 return (data_offset(NULL, mp)); 418 } 419 420 /* 421 * Given a module pointer, return the total size needed for the file. 422 */ 423 static size_t 424 data_size(struct module *mp) 425 { 426 return (data_offset(NULL, mp)); 427 } 428 429 /* 430 * Returns the size needed for all the headers in the file. 431 */ 432 static size_t 433 header_size(void) 434 { 435 return (data_offset(&data_sections[0], NULL)); 436 } 437 438 /* ARGSUSED */ 439 vnode_t * 440 objfs_create_data(vnode_t *pvp) 441 { 442 objfs_odirnode_t *onode = pvp->v_data; 443 vnode_t *vp = gfs_file_create(sizeof (objfs_datanode_t), pvp, 444 objfs_ops_data); 445 objfs_datanode_t *dnode = vp->v_data; 446 447 dnode->objfs_data_gencount = onode->objfs_odir_modctl->mod_gencount; 448 dnode->objfs_data_info.objfs_info_primary = 449 onode->objfs_odir_modctl->mod_prim; 450 451 return (vp); 452 } 453 454 /* ARGSUSED */ 455 static int 456 objfs_data_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, 457 caller_context_t *ct) 458 { 459 struct module *mp; 460 timestruc_t now; 461 462 if ((mp = objfs_data_lock(vp)) == NULL) 463 return (EIO); 464 465 vap->va_type = VREG; 466 vap->va_mode = S_IRUSR | S_IRGRP | S_IROTH; 467 vap->va_nodeid = gfs_file_inode(vp); 468 vap->va_nlink = 1; 469 vap->va_size = data_size(mp); 470 gethrestime(&now); 471 vap->va_atime = vap->va_ctime = vap->va_mtime = now; 472 473 (void) objfs_common_getattr(vp, vap); 474 475 objfs_data_unlock(vp); 476 477 return (0); 478 } 479 480 /* ARGSUSED */ 481 static int 482 objfs_data_access(vnode_t *vp, int mode, int flags, cred_t *cr, 483 caller_context_t *ct) 484 { 485 if (mode & (VWRITE|VEXEC)) 486 return (EACCES); 487 488 return (0); 489 } 490 491 /* ARGSUSED */ 492 int 493 objfs_data_open(vnode_t **cpp, int flag, cred_t *cr, 494 caller_context_t *ct) 495 { 496 if (flag & FWRITE) 497 return (EINVAL); 498 499 return (0); 500 } 501 502 /* 503 * Iterate over all symbols in the table and output each one individually, 504 * converting st_shndx to SHN_ABS for each symbol. 505 */ 506 static int 507 read_symtab(void *addr, size_t size, off_t offset, uio_t *uio) 508 { 509 #ifdef _LP64 510 Elf64_Sym sym, *symtab; 511 #else 512 Elf32_Sym sym, *symtab; 513 #endif 514 off_t index; 515 int error; 516 517 symtab = addr; 518 519 if (offset % sizeof (sym) != 0) { 520 /* 521 * Be careful with the first symbol, as it is not 522 * symbol-aligned. 523 */ 524 off_t partial = offset % sizeof (sym); 525 526 index = offset / sizeof (sym); 527 528 sym = symtab[index]; 529 if (sym.st_shndx != SHN_UNDEF) 530 sym.st_shndx = SHN_ABS; 531 532 if ((error = uiomove((char *)&sym + partial, 533 sizeof (sym) - partial, UIO_READ, uio)) != 0 || 534 uio->uio_resid <= 0) 535 return (error); 536 537 offset = (index + 1) * sizeof (sym); 538 } 539 540 ASSERT(size % sizeof (sym) == 0); 541 542 for (index = offset / sizeof (sym); index < size / sizeof (sym); 543 index++) { 544 545 sym = symtab[index]; 546 if (sym.st_shndx != SHN_UNDEF) 547 sym.st_shndx = SHN_ABS; 548 549 if ((error = uiomove((char *)&sym, sizeof (sym), UIO_READ, 550 uio)) != 0 || uio->uio_resid <= 0) 551 return (error); 552 } 553 554 return (0); 555 } 556 557 /* ARGSUSED */ 558 static int 559 objfs_data_read(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, 560 caller_context_t *ct) 561 { 562 int error = 0; 563 objfs_datanode_t *dnode = vp->v_data; 564 struct module *mp; 565 off_t off; 566 #ifdef _LP64 567 Elf64_Shdr shdr; 568 #else 569 Elf32_Shdr shdr; 570 #endif 571 int i, j; 572 section_desc_t *sp; 573 void *addr; 574 int transidx[NSECTIONS]; 575 576 if ((mp = objfs_data_lock(vp)) == NULL) 577 return (ENOENT); 578 579 if (uio->uio_resid <= 0 || uio->uio_offset >= data_size(mp)) 580 goto error; 581 582 /* 583 * Construct an array to translate from a generic section header index 584 * to an index specific for this object. 585 */ 586 for (i = 0, j = 0; i < NSECTIONS; i++) { 587 transidx[i] = j; 588 if (sect_valid(&data_sections[i], mp)) 589 j++; 590 591 } 592 593 /* 594 * Check to see if we're in the Elf header 595 */ 596 if (uio->uio_loffset < SECTION_OFFSET(0)) { 597 #ifdef _LP64 598 Elf64_Ehdr ehdr; 599 #else 600 Elf32_Ehdr ehdr; 601 #endif 602 603 bzero(&ehdr, sizeof (ehdr)); 604 605 bcopy(ELFMAG, ehdr.e_ident, SELFMAG); 606 #ifdef _BIG_ENDIAN 607 ehdr.e_ident[EI_DATA] = ELFDATA2MSB; 608 #else 609 ehdr.e_ident[EI_DATA] = ELFDATA2LSB; 610 #endif 611 ehdr.e_ident[EI_VERSION] = EV_CURRENT; 612 613 #ifdef _LP64 614 ehdr.e_ident[EI_CLASS] = ELFCLASS64; 615 ehdr.e_type = ELFCLASS64; 616 ehdr.e_ehsize = sizeof (Elf64_Ehdr); 617 ehdr.e_phentsize = sizeof (Elf64_Phdr); 618 ehdr.e_shentsize = sizeof (Elf64_Shdr); 619 #else 620 ehdr.e_ident[EI_CLASS] = ELFCLASS32; 621 ehdr.e_type = ELFCLASS32; 622 ehdr.e_ehsize = sizeof (Elf32_Ehdr); 623 ehdr.e_phentsize = sizeof (Elf32_Phdr); 624 ehdr.e_shentsize = sizeof (Elf32_Shdr); 625 #endif 626 627 #ifdef __sparc 628 #ifdef __sparcv9 629 ehdr.e_machine = EM_SPARCV9; 630 #else 631 ehdr.e_machine = EM_SPARC; 632 #endif 633 #elif defined(__amd64) 634 ehdr.e_machine = EM_AMD64; 635 #else 636 ehdr.e_machine = EM_386; 637 #endif 638 639 ehdr.e_version = EV_CURRENT; 640 ehdr.e_type = ET_SUNWPSEUDO; 641 ehdr.e_shnum = 0; 642 ehdr.e_shoff = SECTION_OFFSET(0); 643 644 for (i = 0; i < NSECTIONS; i++) { 645 if (strcmp(data_sections[i].sect_name, 646 ".shstrtab") == 0) 647 ehdr.e_shstrndx = transidx[i]; 648 649 if (sect_valid(&data_sections[i], mp)) 650 ehdr.e_shnum++; 651 } 652 653 if ((error = uiomove((char *)&ehdr + uio->uio_loffset, 654 sizeof (ehdr) - uio->uio_loffset, UIO_READ, uio)) != 0 || 655 uio->uio_resid <= 0) 656 goto error; 657 } 658 659 /* 660 * Go through and construct section headers for each section. 661 */ 662 j = 0; 663 for (i = 0; i < NSECTIONS; i++) { 664 sp = &data_sections[i]; 665 666 if (!sect_valid(sp, mp)) 667 continue; 668 669 if (uio->uio_loffset < SECTION_OFFSET(j+1)) { 670 shdr.sh_link = transidx[sp->sect_link]; 671 shdr.sh_entsize = sp->sect_entsize; 672 shdr.sh_info = 0; 673 shdr.sh_name = sp->sect_str; 674 shdr.sh_type = sp->sect_type; 675 shdr.sh_flags = sp->sect_flags; 676 shdr.sh_addr = sect_addr(sp, mp); 677 shdr.sh_offset = data_offset(sp, mp); 678 shdr.sh_size = sect_size(sp, mp); 679 shdr.sh_addralign = sp->sect_align; 680 681 off = uio->uio_loffset - SECTION_OFFSET(j); 682 if ((error = uiomove((char *)&shdr + off, 683 sizeof (shdr) - off, UIO_READ, uio)) != 0 || 684 uio->uio_resid <= 0) 685 goto error; 686 } 687 688 j++; 689 } 690 691 /* 692 * Output the data for each section 693 */ 694 for (i = 0; i < NSECTIONS; i++) { 695 size_t nextoff; 696 sp = &data_sections[i]; 697 nextoff = next_offset(i, mp); 698 if (sect_valid(sp, mp) && sp->sect_type != SHT_NOBITS && 699 uio->uio_loffset < nextoff) { 700 701 if (sp->sect_id == SECT_TYPE_INFO) 702 addr = &dnode->objfs_data_info; 703 else 704 addr = (void *)sect_addr(sp, mp); 705 off = uio->uio_loffset - data_offset(sp, mp); 706 707 /* 708 * The symtab requires special processing to convert 709 * the st_shndx field to SHN_ABS. Otherwise, simply 710 * copy the data in bulk. 711 */ 712 if (sp->sect_id == SECT_TYPE_SYMTAB) 713 error = read_symtab(addr, sect_size(sp, mp), 714 off, uio); 715 else 716 error = uiomove((char *)addr + off, 717 sect_size(sp, mp) - off, UIO_READ, uio); 718 719 if (error != 0 || uio->uio_resid <= 0) 720 goto error; 721 722 /* 723 * If the next section needs to be aligned, pad out with 724 * zeroes. 725 */ 726 if (uio->uio_loffset < nextoff) { 727 uint64_t padding = 0; 728 729 ASSERT(nextoff - uio->uio_loffset < 730 sizeof (uint64_t)); 731 732 if ((error = uiomove(&padding, 733 nextoff - uio->uio_loffset, UIO_READ, 734 uio)) != 0 || uio->uio_resid <= 0) 735 goto error; 736 737 } 738 } 739 } 740 741 error: 742 objfs_data_unlock(vp); 743 744 return (error); 745 } 746 747 /* ARGSUSED */ 748 static int 749 objfs_data_seek(vnode_t *vp, offset_t off, offset_t *offp, 750 caller_context_t *ct) 751 { 752 return (0); 753 } 754 755 const fs_operation_def_t objfs_tops_data[] = { 756 { VOPNAME_OPEN, { .vop_open = objfs_data_open } }, 757 { VOPNAME_CLOSE, { .vop_close = objfs_common_close } }, 758 { VOPNAME_IOCTL, { .error = fs_inval } }, 759 { VOPNAME_GETATTR, { .vop_getattr = objfs_data_getattr } }, 760 { VOPNAME_ACCESS, { .vop_access = objfs_data_access } }, 761 { VOPNAME_INACTIVE, { .vop_inactive = gfs_vop_inactive } }, 762 { VOPNAME_READ, { .vop_read = objfs_data_read } }, 763 { VOPNAME_SEEK, { .vop_seek = objfs_data_seek } }, 764 { VOPNAME_MAP, { .vop_map = gfs_vop_map } }, 765 { NULL } 766 }; 767