1/* 2 * Copyright (c) 1999-2008 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* 24 * macNC.c 25 * - macNC boot server 26 * - supports Mac OS 9 AKA Classic netboot clients 27 */ 28 29/* 30 * Modification History: 31 * 32 * December 2, 1997 Dieter Siegmund (dieter@apple.com) 33 * - created 34 * February 1, 1999 Dieter Siegmund (dieter@apple.com) 35 * - create sharepoints at init time (and anytime we get a SIGHUP) 36 * and ensure permissions are correct 37 * November 2, 2000 Dieter Siegmund (dieter@apple.com) 38 * - removed code that creates sharepoints 39 */ 40 41#include <unistd.h> 42#include <stdlib.h> 43#include <sys/types.h> 44#include <sys/stat.h> 45#include <sys/socket.h> 46#include <sys/ioctl.h> 47#include <sys/file.h> 48#include <pwd.h> 49#include <net/if.h> 50#include <netinet/in.h> 51#include <netinet/in_systm.h> 52#include <netinet/ip.h> 53#include <netinet/udp.h> 54#include <netinet/bootp.h> 55#include <netinet/if_ether.h> 56#include <stdio.h> 57#include <strings.h> 58#include <errno.h> 59#include <fcntl.h> 60#include <ctype.h> 61#include <netdb.h> 62#include <syslog.h> 63#include <sys/param.h> 64#include <sys/mount.h> 65#include <arpa/inet.h> 66#include <mach/boolean.h> 67#include <sys/wait.h> 68#include <sys/resource.h> 69#include <ctype.h> 70#include <sys/attr.h> 71 72#include "dhcp.h" 73#include "netinfo.h" 74#include "rfc_options.h" 75#include "subnets.h" 76#include "interfaces.h" 77#include "bootpd.h" 78#include "bsdpd.h" 79#include "macnc_options.h" 80#include "macNC.h" 81#include "host_identifier.h" 82#include "nbsp.h" 83#include "nbimages.h" 84#include "NetBootServer.h" 85#include "util.h" 86 87/* 88 * Function: timestamp_syslog 89 * 90 * Purpose: 91 * Log a timestamped event message to the syslog. 92 */ 93static void 94S_timestamp_syslog(const char * msg) 95{ 96 static struct timeval tvp = {0,0}; 97 struct timeval tv; 98 99 gettimeofday(&tv, 0); 100 if (tvp.tv_sec) { 101 struct timeval result; 102 103 timeval_subtract(tv, tvp, &result); 104 syslog(LOG_INFO, "%ld.%06d (%ld.%06d): %s", 105 tv.tv_sec, tv.tv_usec, result.tv_sec, result.tv_usec, msg); 106 } 107 else 108 syslog(LOG_INFO, "%ld.%06d (%d.%06d): %s", 109 tv.tv_sec, tv.tv_usec, 0, 0, msg); 110 tvp = tv; 111} 112 113 114 115static __inline__ void 116S_timestamp(const char * msg) 117{ 118 if (verbose) 119 S_timestamp_syslog(msg); 120} 121 122static boolean_t 123S_set_dimg_ddsk(const char * path) 124{ 125 struct attrlist attrspec; 126 struct { 127 char type_creator[8]; 128 u_long pad[6]; 129 } finder = { 130 { 'd', 'i', 'm', 'g', 'd', 'd', 's', 'k' }, 131 { 0, 0, 0, 0, 0, 0 } 132 }; 133 134 bzero(&attrspec, sizeof(attrspec)); 135 attrspec.bitmapcount = ATTR_BIT_MAP_COUNT; 136 attrspec.commonattr = ATTR_CMN_FNDRINFO; 137 if (setattrlist(path, &attrspec, &finder, sizeof(finder), 0)) { 138 return (FALSE); 139 } 140 return (TRUE); 141} 142 143static boolean_t 144set_privs_no_stat(const char * path, struct stat * sb_p, uid_t uid, gid_t gid, 145 mode_t mode, boolean_t unlock) 146{ 147 boolean_t needs_chown = FALSE; 148 boolean_t needs_chmod = FALSE; 149 150 if (sb_p->st_uid != uid || sb_p->st_gid != gid) 151 needs_chown = TRUE; 152 153 if ((sb_p->st_mode & ACCESSPERMS) != mode) 154 needs_chmod = TRUE; 155 156 if (needs_chown || needs_chmod) { 157 if (sb_p->st_flags & UF_IMMUTABLE) { 158 if (chflags(path, 0) < 0) 159 return (FALSE); 160 } 161 if (needs_chown) { 162 if (chown(path, uid, gid) < 0) 163 return (FALSE); 164 } 165 if (needs_chmod) { 166 if (chmod(path, mode) < 0) 167 return (FALSE); 168 } 169 } 170 else if (unlock) { 171 if (sb_p->st_flags & UF_IMMUTABLE) { 172 if (chflags(path, 0) < 0) 173 return (FALSE); 174 } 175 } 176 return (TRUE); 177} 178 179boolean_t 180set_privs(const char * path, struct stat * sb_p, uid_t uid, gid_t gid, 181 mode_t mode, boolean_t unlock) 182{ 183 if (stat(path, sb_p) != 0) { 184 return (FALSE); 185 } 186 return (set_privs_no_stat(path, sb_p, uid, gid, mode, unlock)); 187} 188 189/* 190 * Function: S_set_uid_gid 191 * 192 * Purpose: 193 * Given a path to a file, make the owner of both the 194 * enclosing directory and the file itself to user/group uid/gid. 195 */ 196static int 197S_set_uid_gid(const char * file, uid_t uid, gid_t gid) 198{ 199 char dir[PATH_MAX]; 200 char * last_slash = strrchr(file, '/'); 201 202 if (file[0] != '/' || last_slash == NULL) { 203 if (debug) 204 printf("path '%s' is not valid\n", file); 205 return (-1); 206 } 207 208 strncpy(dir, file, last_slash - file); 209 dir[last_slash - file] = '\0'; 210 if (chown(dir, uid, gid) == -1) 211 return (-1); 212 if (chown(file, uid, gid) == -1) 213 return (-1); 214 return (0); 215} 216 217/** 218 ** Other local utility routines: 219 **/ 220 221 222/* 223 * Function: S_get_client_info 224 * 225 * Purpose: 226 * Retrieve the macNC client information from the given packet. 227 * First try to parse the dhcp options, then look for the client 228 * version tag and client info tag. The client info tag will 229 * contain "Apple MacNC". 230 * 231 * Returns: 232 * TRUE and client_version if client version is present in packet 233 * FALSE otherwise 234 */ 235boolean_t 236macNC_get_client_info(struct dhcp * pkt, int pkt_size, dhcpol_t * options, 237 u_int * client_version) 238{ /* get the client version info - if not present, not an NC */ 239 void * client_id; 240 int opt_len; 241 void * vers; 242 243 vers = dhcpol_find(options, macNCtag_client_version_e, 244 &opt_len, NULL); 245 if (vers == NULL) 246 return (FALSE); 247 248 client_id = dhcpol_find(options, macNCtag_client_info_e, 249 &opt_len, NULL); 250 if (client_id == NULL) 251 return (FALSE); 252 if (opt_len != strlen(MACNC_CLIENT_INFO) 253 || bcmp(client_id, MACNC_CLIENT_INFO, opt_len)) { 254 return (FALSE); 255 } 256 if (client_version) 257 *client_version = ntohl(*((unsigned long *)vers)); 258 return (TRUE); 259} 260 261/* 262 * Function: S_get_volpath 263 * 264 * Purpose: 265 * Format a volume pathname given a volume, directory and file name. 266 */ 267static void 268S_get_volpath(char * path, const NBSPEntry * entry, const char * dir, 269 const char * file) 270{ 271 snprintf(path, PATH_MAX, "%s%s%s%s%s", 272 entry->path, 273 (dir && *dir) ? "/" : "", 274 (dir && *dir) ? (char *)dir : "", 275 (file && *file) ? "/" : "", 276 (file && *file) ? (char *)file : ""); 277 return; 278} 279 280/* 281 * Function: S_create_volume_dir 282 * 283 * Purpose: 284 * Create the given directory path on the given volume. 285 */ 286static boolean_t 287S_create_volume_dir(NBSPEntry * entry, char * dirname, mode_t mode) 288{ 289 char path[PATH_MAX]; 290 291 S_get_volpath(path, entry, dirname, NULL); 292 if (create_path(path, mode) < 0) { 293 my_log(LOG_INFO, "macNC: create_volume_dir: create_path(%s)" 294 " failed, %m", path); 295 return (FALSE); 296 } 297 (void)chmod(path, mode); 298 return (TRUE); 299} 300 301/* 302 * Function: set_file_size 303 * 304 * Purpose: 305 * Set a file to be a certain length. 306 */ 307static int 308set_file_size(int fd, off_t size) 309{ 310#ifdef F_SETSIZE 311 fcntl(fd, F_SETSIZE, &size); 312#endif /* F_SETSIZE */ 313 return (ftruncate(fd, size)); 314} 315 316/* 317 * Function: S_create_shadow_file 318 * 319 * Purpose: 320 * Create a new empty file with the right permissions, and optionally, 321 * set to type dimg/ddsk. 322 */ 323static boolean_t 324S_create_shadow_file(const char * shadow_path, uid_t uid, gid_t gid, 325 unsigned long long size, boolean_t set_dimg) 326{ 327 int fd; 328 329 S_set_uid_gid(shadow_path, ROOT_UID, 0); 330 331 fd = open(shadow_path, O_CREAT | O_TRUNC | O_WRONLY, CLIENT_FILE_PERMS); 332 if (fd < 0) { 333 my_log(LOG_INFO, "macNC: couldn't create file '%s': %m", shadow_path); 334 return (FALSE); 335 } 336 if (set_file_size(fd, size)) { 337 my_log(LOG_INFO, "macNC: set_file_size '%s' failed: %m", 338 shadow_path); 339 goto err; 340 } 341 342 if (set_dimg) { 343 if (S_set_dimg_ddsk(shadow_path) == FALSE) { 344 my_log(LOG_INFO, "macNC: set type/creator '%s' failed, %m", 345 shadow_path); 346 goto err; 347 } 348 } 349 350 fchmod(fd, CLIENT_FILE_PERMS); 351 close(fd); 352 353 /* correct the owner of the path */ 354 if (S_set_uid_gid(shadow_path, uid, gid)) { 355 my_log(LOG_INFO, "macNC: setuidgid '%s' to %ld,%ld failed: %m", 356 shadow_path, uid, gid); 357 return (FALSE); 358 } 359 return (TRUE); 360 361 err: 362 close(fd); 363 return (FALSE); 364} 365 366static boolean_t 367S_add_afppath_option(struct in_addr servip, dhcpoa_t * options, 368 NBSPEntry * entry, const char * dir, const char * file, 369 int tag) 370{ 371 char buf[DHCP_OPTION_SIZE_MAX]; 372 dhcpo_err_str_t err; 373 int len; 374 char path[PATH_MAX]; 375 376 if (dir && *dir) 377 snprintf(path, sizeof(path), "%s/%s", dir, file); 378 else { 379 snprintf(path, sizeof(path), "%s", file); 380 } 381 382 len = sizeof(buf); 383 if (macNCopt_encodeAFPPath(servip, AFP_PORT_NUMBER, entry->name, 384 AFP_DIRID_NULL, AFP_PATHTYPE_LONG, 385 path, '/', buf, &len, &err) == FALSE) { 386 my_log(LOG_INFO, "macNC: couldn't encode %s:%s, %s", entry->name, path, 387 err.str); 388 return (FALSE); 389 } 390 if (dhcpoa_add(options, tag, len, buf) != dhcpoa_success_e) { 391 my_log(LOG_INFO, "macNC: couldn't add option %d failed: %s", tag, 392 dhcpoa_err(options)); 393 return (FALSE); 394 } 395 return (TRUE); 396} 397 398 399/* 400 * Function: S_stat_path_vol_file 401 * 402 * Purpose: 403 * Return the stat structure for the given volume/dir/file. 404 */ 405static __inline__ int 406S_stat_path_vol_file(char * path, NBSPEntry * entry, 407 const char * dir, const char * file, 408 struct stat * sb_p) 409{ 410 S_get_volpath(path, entry, dir, file); 411 return (stat(path, sb_p)); 412} 413 414 415static boolean_t 416S_freespace(const char * path, unsigned long long * size) 417{ 418 struct statfs fsb; 419 420 if (statfs(path, &fsb) != 0) { 421 my_log(LOG_INFO, "macNC: statfs on '%s' failed %m", path); 422 return (FALSE); 423 } 424 *size = ((unsigned long long)fsb.f_bavail) 425 * ((unsigned long long)fsb.f_bsize); 426 if (debug) 427 printf("%s %qu x %u = %qu bytes\n", path, 428 fsb.f_bavail, fsb.f_bsize, *size); 429 return (TRUE); 430} 431 432static NBSPEntry * 433S_find_volume_with_space(unsigned long long needspace, int def_vol_index, 434 boolean_t need_hfs) 435{ 436 unsigned long long freespace; 437 int i; 438 NBSPEntry * entry = NULL; 439 char path[PATH_MAX]; 440 int vol_index; 441 442 for (i = 0, vol_index = def_vol_index; i < NBSPList_count(G_client_sharepoints); 443 i++) { 444 NBSPEntry * shp = NBSPList_element(G_client_sharepoints, vol_index); 445 446 if (need_hfs == FALSE || shp->is_hfs == TRUE) { 447 S_get_volpath(path, shp, NULL, NULL); 448 if (S_freespace(path, &freespace) == TRUE) { 449#define SLOP_SPACE_BYTES (20 * 1024 * 1024) 450 /* make sure there's some space left on the volume */ 451 if (freespace >= (needspace + SLOP_SPACE_BYTES)) { 452 entry = shp; 453 if (debug) 454 printf("selected volume %s\n", entry->name); 455 break; /* out of for */ 456 } 457 } 458 } 459 vol_index = (vol_index + 1) % NBSPList_count(G_client_sharepoints); 460 } 461 return (entry); 462} 463 464static boolean_t 465S_remove_shadow(const char * shadow_path, NBSPEntry * entry, const char * dir) 466{ 467 char path[PATH_MAX]; 468 469 /* remove the shadow file */ 470 S_set_uid_gid(shadow_path, ROOT_UID, 0); 471 unlink(shadow_path); 472 S_get_volpath(path, entry, dir, NULL); 473 /* and its directory */ 474 if (rmdir(path)) { 475 char new_path[PATH_MAX]; 476 if (debug) 477 perror(path); 478 S_get_volpath(new_path, entry, "Delete Me", NULL); 479 /* couldn't delete it, try to rename it */ 480 if (rename(path, new_path)) { 481 return (FALSE); 482 } 483 } 484 return (TRUE); 485} 486 487NBSPEntry * 488macNC_allocate_shadow(const char * machine_name, int host_number, 489 uid_t uid, gid_t gid, const char * shadow_name) 490{ 491 int def_vol_index; 492 char dir_path[PATH_MAX]; 493 struct stat dir_statb; 494 int i; 495 char nc_images_dir[PATH_MAX]; 496 NBSPEntry * nc_volume = NULL; 497 unsigned long long needspace; 498 char shadow_path[PATH_MAX]; 499 int vol_index; 500 501 if (G_client_sharepoints == NULL) { 502 syslog(LOG_NOTICE, "macNC_allocate_shadow: no client sharepoints"); 503 return (NULL); 504 } 505 506 strncpy(nc_images_dir, machine_name, sizeof(nc_images_dir)); 507 508 /* attempt to round-robin images across multiple volumes */ 509 def_vol_index = (host_number - 1) % NBSPList_count(G_client_sharepoints); 510 511 /* check all volumes for a client image directory starting at default */ 512 nc_volume = NULL; 513 for (i = 0, vol_index = def_vol_index; i < NBSPList_count(G_client_sharepoints); 514 i++) { 515 NBSPEntry * entry = NBSPList_element(G_client_sharepoints, vol_index); 516 if (S_stat_path_vol_file(dir_path, entry, 517 nc_images_dir, NULL, &dir_statb) == 0) { 518 nc_volume = entry; 519 break; 520 } 521 vol_index = (vol_index + 1) % NBSPList_count(G_client_sharepoints); 522 } 523#define ONE_MEG (1024UL * 1024UL) 524 needspace = ((unsigned long long)G_shadow_size_meg) * ONE_MEG; 525 if (nc_volume != NULL) { 526 struct stat sb_shadow; 527 boolean_t set_file_size = FALSE; 528 boolean_t set_owner_perms = FALSE; 529 530 S_get_volpath(shadow_path, nc_volume, nc_images_dir, 531 shadow_name); 532 if (stat(shadow_path, &sb_shadow) == 0) { /* shadow exists */ 533 S_timestamp("shadow file exists"); 534 if (debug) 535 printf("shadow %qu need %qu\n", 536 sb_shadow.st_size, needspace); 537 538 if (sb_shadow.st_uid != uid 539 || sb_shadow.st_gid != gid 540 || (sb_shadow.st_mode & ACCESSPERMS) != CLIENT_FILE_PERMS) 541 set_owner_perms = TRUE; 542 543 if (sb_shadow.st_size < needspace) { 544 unsigned long long difference; 545 unsigned long long freespace = 0; 546 547 set_file_size = TRUE; 548 S_timestamp("shadow file needs to be grown"); 549 /* check for enough space */ 550 (void)S_freespace(shadow_path, &freespace); 551 difference = (needspace - sb_shadow.st_size); 552 if (freespace < difference) { 553 my_log(LOG_INFO, "macNC: device full, " 554 "attempting to relocate %s", 555 shadow_path); 556 /* blow away the shadow */ 557 if (S_remove_shadow(shadow_path, nc_volume, 558 nc_images_dir) == FALSE) { 559 my_log(LOG_INFO, "macNC: couldn't remove" 560 " shadow %s, %m", shadow_path); 561 goto failed; 562 } 563 /* start fresh */ 564 nc_volume = NULL; 565 } 566 } 567 } 568 else { 569 /* start fresh */ 570 if (S_remove_shadow(shadow_path, nc_volume, nc_images_dir) 571 == FALSE) { 572 my_log(LOG_INFO, "macNC: couldn't remove" 573 " shadow %s, %m", shadow_path); 574 goto failed; 575 } 576 nc_volume = NULL; 577 } 578 if (nc_volume != NULL) { 579 if (set_file_size) { 580 S_timestamp("setting shadow file size"); 581 if (S_create_shadow_file(shadow_path, uid, gid, needspace, 582 FALSE) == FALSE) { 583 my_log(LOG_INFO, "macNC: couldn't create %s, %m", 584 shadow_path); 585 goto failed; 586 } 587 S_timestamp("shadow file size set"); 588 } 589 else if (set_owner_perms) { 590 S_timestamp("setting shadow file perms/owner"); 591 chmod(shadow_path, CLIENT_FILE_PERMS); 592 S_set_uid_gid(shadow_path, uid, gid); 593 S_timestamp("shadow file perms/owner set"); 594 } 595 } 596 } 597 if (nc_volume == NULL) { /* locate the client's image dir */ 598 nc_volume = S_find_volume_with_space(needspace, def_vol_index, FALSE); 599 if (nc_volume == NULL) { 600 if (G_disk_space_warned == FALSE) 601 my_log(LOG_INFO, "macNC: can't create client image: " 602 "OUT OF DISK SPACE"); 603 G_disk_space_warned = TRUE; /* don't keep complaining */ 604 goto failed; 605 } 606 S_get_volpath(shadow_path, nc_volume, nc_images_dir, 607 shadow_name); 608 G_disk_space_warned = FALSE; 609 if (S_create_volume_dir(nc_volume, nc_images_dir, 610 CLIENT_DIR_PERMS) == FALSE) { 611 goto failed; 612 } 613 if (S_create_shadow_file(shadow_path, uid, gid, needspace, 614 FALSE) == FALSE) { 615 my_log(LOG_INFO, "macNC: couldn't create %s, %m", 616 shadow_path); 617 goto failed; 618 } 619 } 620 return (nc_volume); 621 622 failed: 623 return (NULL); 624} 625 626 627/* 628 * Function: S_add_image_options 629 * 630 * Purpose: 631 * Create/initialize image for client, format the paths into the 632 * response options. 633 */ 634static boolean_t 635S_add_image_options(NBImageEntryRef image_entry, 636 uid_t uid, gid_t gid, struct in_addr servip, 637 dhcpoa_t * options, int host_number, 638 const char * afp_hostname) 639{ 640 int def_vol_index; 641 char dir_path[PATH_MAX]; 642 struct stat dir_statb; 643 int i; 644 char nc_images_dir[PATH_MAX]; 645 NBSPEntry * nc_volume = NULL; 646 char path[PATH_MAX]; 647 struct stat statb; 648 int vol_index; 649 650 /* make sure the bootfile exists and the permissions are correct */ 651 snprintf(path, sizeof(path), "%s/%s/%s", 652 image_entry->sharepoint->path, 653 image_entry->dir_name, 654 image_entry->bootfile); 655 if (set_privs(path, &statb, ROOT_UID, G_admin_gid, 656 SHARED_FILE_PERMS, FALSE) == FALSE) { 657 syslog(LOG_INFO, "macNC: '%s' does not exist", path); 658 return (FALSE); 659 } 660 661 snprintf(nc_images_dir, sizeof(nc_images_dir), 662 "%s", afp_hostname); 663 664 /* attempt to round-robin images across multiple volumes */ 665 def_vol_index = (host_number - 1) % NBSPList_count(G_client_sharepoints); 666 667 /* check all volumes for a client image directory starting at default */ 668 nc_volume = NULL; 669 for (i = 0, vol_index = def_vol_index; i < NBSPList_count(G_client_sharepoints); 670 i++) { 671 NBSPEntry * entry = NBSPList_element(G_client_sharepoints, vol_index); 672 if (entry->is_hfs == TRUE 673 && S_stat_path_vol_file(dir_path, entry, 674 nc_images_dir, NULL, &dir_statb) == 0) { 675 nc_volume = entry; 676 break; 677 } 678 vol_index = (vol_index + 1) % NBSPList_count(G_client_sharepoints); 679 } 680 681 /* if the client has its own private copy of the image file, use it */ 682 if (nc_volume != NULL 683 && S_stat_path_vol_file(path, nc_volume, nc_images_dir, 684 image_entry->type_info.classic.shared, 685 &statb) == 0) { 686 /* set the image file perms */ 687 if (set_privs_no_stat(path, &statb, uid, gid, CLIENT_FILE_PERMS, 688 TRUE) == FALSE) { 689 my_log(LOG_INFO, "macNC: couldn't set permissions on path %s: %m", 690 path); 691 return (FALSE); 692 } 693 /* set the client dir perms */ 694 if (set_privs_no_stat(dir_path, &dir_statb, uid, gid, 695 CLIENT_DIR_PERMS, TRUE) == FALSE) { 696 my_log(LOG_INFO, "macNC: couldn't set permissions on path %s: %m", 697 dir_path); 698 return (FALSE); 699 } 700 if (S_add_afppath_option(servip, options, nc_volume, nc_images_dir, 701 image_entry->type_info.classic.shared, 702 macNCtag_shared_system_file_e) == FALSE) { 703 return (FALSE); 704 } 705 /* does the client have its own Private image? */ 706 if (image_entry->type_info.classic.private != NULL) { 707 if (S_stat_path_vol_file(path, nc_volume, nc_images_dir, 708 image_entry->type_info.classic.private, 709 &statb) == 0) { 710 if (set_privs_no_stat(path, &statb, uid, gid, 711 CLIENT_FILE_PERMS, TRUE) == FALSE) { 712 my_log(LOG_INFO, 713 "macNC: couldn't set permissions on path %s: %m", 714 path); 715 return (FALSE); 716 } 717 /* 718 * We use macNCtag_page_file_e instead of 719 * macNCtag_private_system_file_e as you would expect. 720 * The reason is that the client ROM software assumes 721 * that the private_system_file is read-only. It also 722 * assumes that page_file is read-write. Since we don't 723 * use page_file for anything else, we use that instead. 724 * This is a hack/workaround. 725 */ 726 if (S_add_afppath_option(servip, options, nc_volume, 727 nc_images_dir, 728 image_entry->type_info.classic.private, 729 macNCtag_page_file_e) == FALSE){ 730 return (FALSE); 731 } 732 } 733 } 734 } 735 else { /* client gets shadow file(s) */ 736 unsigned long long needspace; 737 char private_path[PATH_MAX]; 738 char shadow_path[PATH_MAX]; 739 char shared_path[PATH_MAX]; 740 741 snprintf(shared_path, sizeof(shared_path), 742 "%s/%s/%s", 743 image_entry->sharepoint->path, 744 image_entry->dir_name, 745 image_entry->type_info.classic.shared); 746 /* set the shared system image permissions */ 747 if (set_privs(shared_path, &statb, ROOT_UID, G_admin_gid, 748 SHARED_FILE_PERMS, FALSE) == FALSE) { 749 syslog(LOG_INFO, "macNC: '%s' does not exist", shared_path); 750 return (FALSE); 751 } 752 753 /* add the shared system image option */ 754 if (S_add_afppath_option(servip, options, 755 image_entry->sharepoint, 756 image_entry->dir_name, 757 image_entry->type_info.classic.shared, 758 macNCtag_shared_system_file_e) == FALSE) { 759 return (FALSE); 760 } 761 if (image_entry->type_info.classic.private != NULL) { 762 /* check for the private system image, set its permissions */ 763 snprintf(private_path, sizeof(private_path), 764 "%s/%s/%s", 765 image_entry->sharepoint->path, 766 image_entry->dir_name, 767 image_entry->type_info.classic.private); 768 if (set_privs(private_path, &statb, ROOT_UID, G_admin_gid, 769 SHARED_FILE_PERMS, FALSE) == TRUE) { 770 /* add the private image option */ 771 if (S_add_afppath_option(servip, options, 772 image_entry->sharepoint, 773 image_entry->dir_name, 774 image_entry->type_info.classic.private, 775 macNCtag_private_system_file_e) 776 == FALSE) { 777 return (FALSE); 778 } 779 } 780 } 781 782#define ONE_MEG (1024UL * 1024UL) 783 needspace = ((unsigned long long)G_shadow_size_meg) * ONE_MEG; 784 if (nc_volume != NULL) { 785 struct stat sb_shadow; 786 boolean_t set_file_size = FALSE; 787 boolean_t set_owner_perms = FALSE; 788 789 S_get_volpath(shadow_path, nc_volume, nc_images_dir, 790 kNetBootShadowName); 791 if (stat(shadow_path, &sb_shadow) == 0) { /* shadow exists */ 792 S_timestamp("shadow file exists"); 793 if (debug) 794 printf("shadow %qu need %qu\n", 795 sb_shadow.st_size, needspace); 796 797 if (sb_shadow.st_uid != uid 798 || sb_shadow.st_gid != gid 799 || (sb_shadow.st_mode & ACCESSPERMS) != CLIENT_FILE_PERMS) 800 set_owner_perms = TRUE; 801 802 if (sb_shadow.st_size < needspace) { 803 unsigned long long difference; 804 unsigned long long freespace = 0; 805 806 set_file_size = TRUE; 807 S_timestamp("shadow file needs to be grown"); 808 /* check for enough space */ 809 (void)S_freespace(shadow_path, &freespace); 810 difference = (needspace - sb_shadow.st_size); 811 if (freespace < difference) { 812 my_log(LOG_INFO, "macNC: device full, " 813 "attempting to relocate %s", 814 shadow_path); 815 /* blow away the shadow */ 816 if (S_remove_shadow(shadow_path, nc_volume, 817 nc_images_dir) == FALSE) { 818 my_log(LOG_INFO, "macNC: couldn't remove" 819 " shadow %s, %m", shadow_path); 820 return (FALSE); 821 } 822 /* start fresh */ 823 nc_volume = NULL; 824 } 825 } 826 } 827 else { 828 /* start fresh */ 829 if (S_remove_shadow(shadow_path, nc_volume, nc_images_dir) 830 == FALSE) { 831 my_log(LOG_INFO, "macNC: couldn't remove" 832 " shadow %s, %m", shadow_path); 833 return (FALSE); 834 } 835 nc_volume = NULL; 836 } 837 if (nc_volume != NULL) { 838 if (set_file_size) { 839 S_timestamp("setting shadow file size"); 840 if (S_create_shadow_file(shadow_path, uid, gid, needspace, 841 TRUE) 842 == FALSE) { 843 my_log(LOG_INFO, "macNC: couldn't create %s, %m", 844 shadow_path); 845 return (FALSE); 846 } 847 S_timestamp("shadow file size set"); 848 } 849 else { 850 if (set_owner_perms) { 851 S_timestamp("setting shadow file perms/owner"); 852 chmod(shadow_path, CLIENT_FILE_PERMS); 853 S_set_uid_gid(shadow_path, uid, gid); 854 } 855 S_set_dimg_ddsk(shadow_path); 856 S_timestamp("shadow file perms/owner set"); 857 } 858 } 859 } 860 if (nc_volume == NULL) { /* locate the client's image dir */ 861 nc_volume = S_find_volume_with_space(needspace, def_vol_index, 862 TRUE); 863 if (nc_volume == NULL) { 864 if (G_disk_space_warned == FALSE) 865 my_log(LOG_INFO, "macNC: can't create client image: " 866 "OUT OF DISK SPACE"); 867 G_disk_space_warned = TRUE; /* don't keep complaining */ 868 return (FALSE); 869 } 870 S_get_volpath(shadow_path, nc_volume, nc_images_dir, 871 kNetBootShadowName); 872 G_disk_space_warned = FALSE; 873 if (S_create_volume_dir(nc_volume, nc_images_dir, 874 CLIENT_DIR_PERMS) == FALSE) { 875 return (FALSE); 876 } 877 if (S_create_shadow_file(shadow_path, uid, gid, needspace, 878 TRUE) == FALSE) { 879 my_log(LOG_INFO, "macNC: couldn't create %s, %m", 880 shadow_path); 881 return (FALSE); 882 } 883 } 884 885 /* add the shadow file option */ 886 if (S_add_afppath_option(servip, options, nc_volume, 887 nc_images_dir, kNetBootShadowName, 888 macNCtag_shared_system_shadow_file_e) 889 == FALSE) { 890 return (FALSE); 891 } 892 } 893 return (TRUE); 894} 895 896boolean_t 897macNC_allocate(NBImageEntryRef image_entry, 898 struct dhcp * reply, const char * hostname, 899 struct in_addr servip, int host_number, dhcpoa_t * options, 900 uid_t uid, const char * afp_user, const char * passwd) 901{ 902 if (G_client_sharepoints == NULL) { 903 syslog(LOG_NOTICE, "macNC_allocate: no client sharepoints"); 904 return (FALSE); 905 } 906 907 if (dhcpoa_add(options, macNCtag_user_name_e, strlen(afp_user), 908 afp_user) != dhcpoa_success_e) { 909 my_log(LOG_INFO, 910 "macNC: afp user name option add %s failed, %s", 911 afp_user, dhcpoa_err(options)); 912 return (FALSE); 913 } 914 /* add the Mac OS machine name option */ 915 if (dhcpoa_add(options, macNCtag_MacOS_machine_name_e, 916 strlen(hostname), hostname) != dhcpoa_success_e) { 917 my_log(LOG_INFO, 918 "macNC: machine name option add client %s failed, %s", 919 hostname, dhcpoa_err(options)); 920 return (FALSE); 921 } 922 { 923 char buf[16]; 924 int buf_len = sizeof(buf); 925 926 if (macNCopt_str_to_type(passwd, macNCtype_afp_password_e, 927 buf, &buf_len, NULL) == FALSE 928 || dhcpoa_add(options, macNCtag_password_e, 929 buf_len, buf) != dhcpoa_success_e) { 930 my_log(LOG_INFO, "macNC: failed add afp password for %d", 931 host_number); 932 return (FALSE); 933 } 934 } 935 if (S_add_image_options(image_entry, uid, G_admin_gid, servip, 936 options, host_number, hostname) == FALSE) { 937 my_log(LOG_INFO, 938 "macNC: S_add_image_options for %s failed", afp_user); 939 return (FALSE); 940 } 941 return (TRUE); 942} 943 944void 945macNC_unlink_shadow(int host_number, const char * hostname) 946{ 947 int def_vol_index; 948 int i; 949 char nc_images_dir[PATH_MAX]; 950 NBSPEntry * nc_volume = NULL; 951 struct stat shadow_statb; 952 char shadow_path[PATH_MAX]; 953 int vol_index; 954 955 if (G_client_sharepoints == NULL) { 956 return; 957 } 958 snprintf(nc_images_dir, sizeof(nc_images_dir), 959 "%s", hostname); 960 961 def_vol_index = (host_number - 1) % NBSPList_count(G_client_sharepoints); 962 963 /* check all volumes for a client image directory starting at default */ 964 nc_volume = NULL; 965 for (i = 0, vol_index = def_vol_index; 966 i < NBSPList_count(G_client_sharepoints); i++) { 967 NBSPEntry * entry = NBSPList_element(G_client_sharepoints, vol_index); 968 if (S_stat_path_vol_file(shadow_path, entry, nc_images_dir, 969 kNetBootShadowName, &shadow_statb) == 0) { 970 if (unlink(shadow_path) < 0) { 971 my_log(LOG_DEBUG, 972 "macNC: unlink(%s) failed, %m", shadow_path); 973 } 974 return; 975 } 976 vol_index = (vol_index + 1) % NBSPList_count(G_client_sharepoints); 977 } 978 return; 979} 980