1*0f1762c0SRobert Mustacchi#!/usr/bin/ksh 2*0f1762c0SRobert Mustacchi# 3*0f1762c0SRobert Mustacchi# This file and its contents are supplied under the terms of the 4*0f1762c0SRobert Mustacchi# Common Development and Distribution License ("CDDL"), version 1.0. 5*0f1762c0SRobert Mustacchi# You may only use this file in accordance with the terms of version 6*0f1762c0SRobert Mustacchi# 1.0 of the CDDL. 7*0f1762c0SRobert Mustacchi# 8*0f1762c0SRobert Mustacchi# A full copy of the text of the CDDL should have accompanied this 9*0f1762c0SRobert Mustacchi# source. A copy of the CDDL is also available via the Internet at 10*0f1762c0SRobert Mustacchi# http://www.illumos.org/license/CDDL. 11*0f1762c0SRobert Mustacchi# 12*0f1762c0SRobert Mustacchi 13*0f1762c0SRobert Mustacchi# 14*0f1762c0SRobert Mustacchi# Copyright 2026 Oxide Computer Company 15*0f1762c0SRobert Mustacchi# 16*0f1762c0SRobert Mustacchi 17*0f1762c0SRobert Mustacchi# 18*0f1762c0SRobert Mustacchi# This program goes through and tests how ln acts by default and with its -L and 19*0f1762c0SRobert Mustacchi# -P flags as well as the default behavior. The basic summary is: 20*0f1762c0SRobert Mustacchi# 21*0f1762c0SRobert Mustacchi# 1. All of these flags should be ignored when used with -s. 22*0f1762c0SRobert Mustacchi# 2. -L should cause us to dereference a symlink when making a hardlink. That is 23*0f1762c0SRobert Mustacchi# we should get a hardlink to the underlying object (if allowed). 24*0f1762c0SRobert Mustacchi# 3. -P should cause us to get a hardlink to the symlink itself. 25*0f1762c0SRobert Mustacchi# 4. /usr/bin/ln defaults to -P behavior. /usr/xpg4/bin/ln defaults to -L 26*0f1762c0SRobert Mustacchi# behavior. 27*0f1762c0SRobert Mustacchi# 28*0f1762c0SRobert Mustacchi# Finally, we want to see how this works across a variety of symlinks to the 29*0f1762c0SRobert Mustacchi# following file types: regular files, directories, doors, fifos, unix domain 30*0f1762c0SRobert Mustacchi# sockets. We must be very careful not to create hardlinks to directories here 31*0f1762c0SRobert Mustacchi# as tests are sometimes run by privileged users. 32*0f1762c0SRobert Mustacchi# 33*0f1762c0SRobert Mustacchi 34*0f1762c0SRobert Mustacchiunalias -a 35*0f1762c0SRobert Mustacchiset -o pipefail 36*0f1762c0SRobert Mustacchiexport LANG=C.UTF-8 37*0f1762c0SRobert Mustacchi 38*0f1762c0SRobert MustacchiLN=${LN:-"/usr/bin/ln"} 39*0f1762c0SRobert MustacchiXLN=${XLN:-"/usr/xpg4/bin/ln"} 40*0f1762c0SRobert Mustacchi 41*0f1762c0SRobert Mustacchilnlp_exit=0 42*0f1762c0SRobert Mustacchilnlp_arg0=$(basename $0) 43*0f1762c0SRobert Mustacchilnlp_tdir=$(dirname $0) 44*0f1762c0SRobert Mustacchilnlp_mkobj="$lnlp_tdir/mkobj" 45*0f1762c0SRobert Mustacchilnlp_equiv="$lnlp_tdir/equiv" 46*0f1762c0SRobert Mustacchilnlp_work="/tmp/$lnlp_arg0.$$" 47*0f1762c0SRobert Mustacchi 48*0f1762c0SRobert Mustacchi# 49*0f1762c0SRobert Mustacchi# The following table describes the files that we're testing against. hardlinks 50*0f1762c0SRobert Mustacchi# will not work with doors as doors are considered to be on a different file 51*0f1762c0SRobert Mustacchi# system. Directories will fail because we are running as a non-root user to 52*0f1762c0SRobert Mustacchi# ensure that we don't create hardlink to directory madness. 53*0f1762c0SRobert Mustacchi# 54*0f1762c0SRobert Mustacchitypeset -A lnlp_files=( 55*0f1762c0SRobert Mustacchi ["file"]=(make="touch" hard="pass" soft="pass") 56*0f1762c0SRobert Mustacchi ["dir"]=(make="mkdir" hard="fail" soft="pass") 57*0f1762c0SRobert Mustacchi ["fifo"]=(make="$lnlp_mkobj -f" hard="pass" soft="pass") 58*0f1762c0SRobert Mustacchi ["door"]=(make="$lnlp_mkobj -d" hard="fail" soft="pass") 59*0f1762c0SRobert Mustacchi ["uds"]=(make="$lnlp_mkobj -s" hard="pass" soft="pass") 60*0f1762c0SRobert Mustacchi) 61*0f1762c0SRobert Mustacchi 62*0f1762c0SRobert Mustacchifunction fatal 63*0f1762c0SRobert Mustacchi{ 64*0f1762c0SRobert Mustacchi typeset msg="$*" 65*0f1762c0SRobert Mustacchi echo "TEST FAILED: $msg" >&2 66*0f1762c0SRobert Mustacchi exit 1 67*0f1762c0SRobert Mustacchi} 68*0f1762c0SRobert Mustacchi 69*0f1762c0SRobert Mustacchifunction warn 70*0f1762c0SRobert Mustacchi{ 71*0f1762c0SRobert Mustacchi typeset msg="$*" 72*0f1762c0SRobert Mustacchi echo "TEST FAILED: $msg" >&2 73*0f1762c0SRobert Mustacchi lnlp_exit=1 74*0f1762c0SRobert Mustacchi} 75*0f1762c0SRobert Mustacchi 76*0f1762c0SRobert Mustacchifunction cleanup 77*0f1762c0SRobert Mustacchi{ 78*0f1762c0SRobert Mustacchi rm -rf $lnlp_work/ 79*0f1762c0SRobert Mustacchi} 80*0f1762c0SRobert Mustacchi 81*0f1762c0SRobert Mustacchi# 82*0f1762c0SRobert Mustacchi# Create the series of objects and symlinks that we expect to exist. 83*0f1762c0SRobert Mustacchi# 84*0f1762c0SRobert Mustacchifunction setup 85*0f1762c0SRobert Mustacchi{ 86*0f1762c0SRobert Mustacchi mkdir "$lnlp_work" || fatal "failed to make test directory" 87*0f1762c0SRobert Mustacchi for f in ${!lnlp_files[@]}; do 88*0f1762c0SRobert Mustacchi typeset targ="${lnlp_work}/$f" 89*0f1762c0SRobert Mustacchi typeset sym="${targ}_symlink" 90*0f1762c0SRobert Mustacchi 91*0f1762c0SRobert Mustacchi ${lnlp_files[$f].make} $targ || fatal "failed to make $f" 92*0f1762c0SRobert Mustacchi ln -s $targ $sym || fatal "failed to create symlink to $f" 93*0f1762c0SRobert Mustacchi done 94*0f1762c0SRobert Mustacchi} 95*0f1762c0SRobert Mustacchi 96*0f1762c0SRobert Mustacchi# 97*0f1762c0SRobert Mustacchi# Run a single ln hardlink invocation. This invocation is expected to pass. $dst 98*0f1762c0SRobert Mustacchi# should match the contents of $exp. 99*0f1762c0SRobert Mustacchi# 100*0f1762c0SRobert Mustacchifunction test_one_hl 101*0f1762c0SRobert Mustacchi{ 102*0f1762c0SRobert Mustacchi typeset desc="$1" 103*0f1762c0SRobert Mustacchi typeset src="$lnlp_work/${2}_symlink" 104*0f1762c0SRobert Mustacchi typeset exp_base="$3" 105*0f1762c0SRobert Mustacchi typeset exp="$lnlp_work/$3" 106*0f1762c0SRobert Mustacchi typeset dst="$lnlp_work/test" 107*0f1762c0SRobert Mustacchi 108*0f1762c0SRobert Mustacchi # 109*0f1762c0SRobert Mustacchi # Remaining arguments after this are the correct ln program and flags to 110*0f1762c0SRobert Mustacchi # use. 111*0f1762c0SRobert Mustacchi # 112*0f1762c0SRobert Mustacchi shift; shift; shift 113*0f1762c0SRobert Mustacchi 114*0f1762c0SRobert Mustacchi rm -f $dst 115*0f1762c0SRobert Mustacchi if ! $* "$src" "$dst"; then 116*0f1762c0SRobert Mustacchi warn "$desc: $* $src $dst failed unexpectedly" 117*0f1762c0SRobert Mustacchi return 118*0f1762c0SRobert Mustacchi fi 119*0f1762c0SRobert Mustacchi 120*0f1762c0SRobert Mustacchi if ! $lnlp_equiv $exp $dst; then 121*0f1762c0SRobert Mustacchi warn "$desc: ln didn't result in expected file $3" 122*0f1762c0SRobert Mustacchi return 123*0f1762c0SRobert Mustacchi fi 124*0f1762c0SRobert Mustacchi 125*0f1762c0SRobert Mustacchi printf "TEST PASSED: %s\n" "$desc" 126*0f1762c0SRobert Mustacchi} 127*0f1762c0SRobert Mustacchi 128*0f1762c0SRobert Mustacchi# 129*0f1762c0SRobert Mustacchi# This is variant where the ln results should fail. This is generally used when 130*0f1762c0SRobert Mustacchi# using hardlinks on doors and directories. 131*0f1762c0SRobert Mustacchi# 132*0f1762c0SRobert Mustacchifunction test_one_fail 133*0f1762c0SRobert Mustacchi{ 134*0f1762c0SRobert Mustacchi typeset desc="$1" 135*0f1762c0SRobert Mustacchi typeset src="$lnlp_work/${2}_symlink" 136*0f1762c0SRobert Mustacchi typeset dst="$lnlp_work/test" 137*0f1762c0SRobert Mustacchi 138*0f1762c0SRobert Mustacchi shift; shift 139*0f1762c0SRobert Mustacchi 140*0f1762c0SRobert Mustacchi rm -f $dst 141*0f1762c0SRobert Mustacchi if $* "$src" "$src" 2>/dev/null; then 142*0f1762c0SRobert Mustacchi warn "$desc: $* unexpectedly worked?!" 143*0f1762c0SRobert Mustacchi return 144*0f1762c0SRobert Mustacchi fi 145*0f1762c0SRobert Mustacchi 146*0f1762c0SRobert Mustacchi printf "TEST PASSED: %s failed correctly\n" "$desc" 147*0f1762c0SRobert Mustacchi} 148*0f1762c0SRobert Mustacchi 149*0f1762c0SRobert Mustacchi# 150*0f1762c0SRobert Mustacchi# For a given version of ln and its options, run through each of the different 151*0f1762c0SRobert Mustacchi# valid file types and see if it passes or fails. 152*0f1762c0SRobert Mustacchi# 153*0f1762c0SRobert Mustacchifunction test_series 154*0f1762c0SRobert Mustacchi{ 155*0f1762c0SRobert Mustacchi typeset bdesc="$1" 156*0f1762c0SRobert Mustacchi typeset rtype="$2" 157*0f1762c0SRobert Mustacchi 158*0f1762c0SRobert Mustacchi # 159*0f1762c0SRobert Mustacchi # Options after this will be the flags and type of ln invocation we 160*0f1762c0SRobert Mustacchi # should use. 161*0f1762c0SRobert Mustacchi # 162*0f1762c0SRobert Mustacchi shift; shift 163*0f1762c0SRobert Mustacchi for f in ${!lnlp_files[@]}; do 164*0f1762c0SRobert Mustacchi typeset test_exp 165*0f1762c0SRobert Mustacchi 166*0f1762c0SRobert Mustacchi if [[ "$rtype" == "hard" ]]; then 167*0f1762c0SRobert Mustacchi test_exp="$f" 168*0f1762c0SRobert Mustacchi else 169*0f1762c0SRobert Mustacchi test_exp="${f}_symlink" 170*0f1762c0SRobert Mustacchi fi 171*0f1762c0SRobert Mustacchi 172*0f1762c0SRobert Mustacchi if [[ ${lnlp_files[$f].[$rtype]} == "pass" ]]; then 173*0f1762c0SRobert Mustacchi test_one_hl "$bdesc: $f results in $rtype" $f \ 174*0f1762c0SRobert Mustacchi $test_exp $* 175*0f1762c0SRobert Mustacchi else 176*0f1762c0SRobert Mustacchi test_one_fail "$bdesc: $f ${rtype}link fails" $f $* 177*0f1762c0SRobert Mustacchi fi 178*0f1762c0SRobert Mustacchi done 179*0f1762c0SRobert Mustacchi} 180*0f1762c0SRobert Mustacchi 181*0f1762c0SRobert Mustacchi# 182*0f1762c0SRobert Mustacchi# Go through and make a symlink to each file and verify that it is a different 183*0f1762c0SRobert Mustacchi# inode than one that already exists. We skip doing this for every combination 184*0f1762c0SRobert Mustacchi# and just do it once for a file and a symlink. 185*0f1762c0SRobert Mustacchi# 186*0f1762c0SRobert Mustacchifunction test_symlink 187*0f1762c0SRobert Mustacchi{ 188*0f1762c0SRobert Mustacchi typeset dst="$lnlp_work/test" 189*0f1762c0SRobert Mustacchi typeset src="$lnlp_work/file" 190*0f1762c0SRobert Mustacchi typeset desc="$1" 191*0f1762c0SRobert Mustacchi shift 192*0f1762c0SRobert Mustacchi 193*0f1762c0SRobert Mustacchi rm -f $dst 194*0f1762c0SRobert Mustacchi if ! $* $src $dst; then 195*0f1762c0SRobert Mustacchi warn "$desc: $* $src $dst unexpectedly failed" 196*0f1762c0SRobert Mustacchi return 197*0f1762c0SRobert Mustacchi fi 198*0f1762c0SRobert Mustacchi 199*0f1762c0SRobert Mustacchi if $lnlp_equiv $src $dst 1>/dev/null 2>/dev/null; then 200*0f1762c0SRobert Mustacchi warn "$desc: ln -s somehow ended up with the same inode" 201*0f1762c0SRobert Mustacchi return 202*0f1762c0SRobert Mustacchi fi 203*0f1762c0SRobert Mustacchi 204*0f1762c0SRobert Mustacchi src="$lnlp_work/file_symlink" 205*0f1762c0SRobert Mustacchi rm -f $dst 206*0f1762c0SRobert Mustacchi if ! $* $src $dst; then 207*0f1762c0SRobert Mustacchi warn "$desc: $* $src $dst unexpectedly failed" 208*0f1762c0SRobert Mustacchi return 209*0f1762c0SRobert Mustacchi fi 210*0f1762c0SRobert Mustacchi 211*0f1762c0SRobert Mustacchi if $lnlp_equiv $src $dst 1>/dev/null 2>/dev/null; then 212*0f1762c0SRobert Mustacchi warn "$desc: ln -s somehow ended up with the same inode" 213*0f1762c0SRobert Mustacchi return 214*0f1762c0SRobert Mustacchi fi 215*0f1762c0SRobert Mustacchi 216*0f1762c0SRobert Mustacchi printf "TEST PASSED: %s\n" "$desc" 217*0f1762c0SRobert Mustacchi} 218*0f1762c0SRobert Mustacchi 219*0f1762c0SRobert Mustacchi# 220*0f1762c0SRobert Mustacchi# Sanity check that we're not running as a privileged user. This won't catch 221*0f1762c0SRobert Mustacchi# some some cases where we have privileges, but this is better than nothing. 222*0f1762c0SRobert Mustacchi# 223*0f1762c0SRobert Mustacchilnlp_uid=$(id -u) 224*0f1762c0SRobert Mustacchiif (( lnlp_uid == 0 )); then 225*0f1762c0SRobert Mustacchi printf "Running as uid 0 is not permitted try nobody instead\n" >&2 226*0f1762c0SRobert Mustacchi exit 1 227*0f1762c0SRobert Mustacchifi 228*0f1762c0SRobert Mustacchi 229*0f1762c0SRobert Mustacchitrap cleanup EXIT 230*0f1762c0SRobert Mustacchi 231*0f1762c0SRobert Mustacchi# 232*0f1762c0SRobert Mustacchi# Create all of our different fields and the symlinks to them. 233*0f1762c0SRobert Mustacchi# 234*0f1762c0SRobert Mustacchisetup 235*0f1762c0SRobert Mustacchi 236*0f1762c0SRobert Mustacchi# 237*0f1762c0SRobert Mustacchi# First test the defaults of each command. 238*0f1762c0SRobert Mustacchi# 239*0f1762c0SRobert Mustacchitest_series "$LN defaults" soft $LN 240*0f1762c0SRobert Mustacchitest_series "$XLN defaults" hard $XLN 241*0f1762c0SRobert Mustacchi 242*0f1762c0SRobert Mustacchi# 243*0f1762c0SRobert Mustacchi# Now verify that they do identical thing with a single -L and -P. 244*0f1762c0SRobert Mustacchi# 245*0f1762c0SRobert Mustacchitest_series "$LN -P" soft $LN -P 246*0f1762c0SRobert Mustacchitest_series "$LN -L" hard $LN -L 247*0f1762c0SRobert Mustacchi 248*0f1762c0SRobert Mustacchitest_series "$LN -P wins (-LP)" soft $LN -LP 249*0f1762c0SRobert Mustacchitest_series "$LN -P wins (-PLPLP)" soft $LN -PLPLP 250*0f1762c0SRobert Mustacchitest_series "$LN -P wins (-LLLP)" soft $LN -LLLP 251*0f1762c0SRobert Mustacchitest_series "$LN -L wins (-PL)" hard $LN -PL 252*0f1762c0SRobert Mustacchitest_series "$LN -L wins (-LPLPL)" hard $LN -LPLPL 253*0f1762c0SRobert Mustacchitest_series "$LN -L wins (-PPPL)" hard $LN -PPPL 254*0f1762c0SRobert Mustacchi 255*0f1762c0SRobert Mustacchitest_series "$XLN -P wins (-LP)" soft $XLN -LP 256*0f1762c0SRobert Mustacchitest_series "$XLN -P wins (-PLPLP)" soft $XLN -PLPLP 257*0f1762c0SRobert Mustacchitest_series "$XLN -P wins (-LLLP)" soft $XLN -LLLP 258*0f1762c0SRobert Mustacchitest_series "$XLN -L wins (-PL)" hard $XLN -PL 259*0f1762c0SRobert Mustacchitest_series "$XLN -L wins (-LPLPL)" hard $XLN -LPLPL 260*0f1762c0SRobert Mustacchitest_series "$XLN -L wins (-PPPL)" hard $XLN -PPPL 261*0f1762c0SRobert Mustacchi 262*0f1762c0SRobert Mustacchi# 263*0f1762c0SRobert Mustacchi# Go through and manually do a few symlink related tests. 264*0f1762c0SRobert Mustacchi# 265*0f1762c0SRobert Mustacchitest_symlink "$LN -s" $LN -s 266*0f1762c0SRobert Mustacchitest_symlink "$LN -s -L" $LN -s -L 267*0f1762c0SRobert Mustacchitest_symlink "$LN -s -P" $LN -s -P 268*0f1762c0SRobert Mustacchitest_symlink "$LN -s -LP" $LN -s -LP 269*0f1762c0SRobert Mustacchitest_symlink "$LN -s -PL" $LN -s -PL 270*0f1762c0SRobert Mustacchi 271*0f1762c0SRobert Mustacchitest_symlink "$XLN -s" $XLN -s 272*0f1762c0SRobert Mustacchitest_symlink "$XLN -s -L" $XLN -s -L 273*0f1762c0SRobert Mustacchitest_symlink "$XLN -s -P" $XLN -s -P 274*0f1762c0SRobert Mustacchitest_symlink "$XLN -s -LP" $XLN -s -LP 275*0f1762c0SRobert Mustacchitest_symlink "$XLN -s -PL" $XLN -s -PL 276*0f1762c0SRobert Mustacchi 277*0f1762c0SRobert Mustacchiif (( lnlp_exit == 0 )); then 278*0f1762c0SRobert Mustacchi printf "All tests passed successfully\n" 279*0f1762c0SRobert Mustacchifi 280*0f1762c0SRobert Mustacchiexit $lnlp_exit 281