From ea4a2aac2d4cab0ec45799b02565bee36806c3f2 Mon Sep 17 00:00:00 2001 From: Mark Verboom Date: Thu, 7 Apr 2022 09:07:14 +0200 Subject: [PATCH] Different and cleaner implementation of more atomic file attribute change (credits to Steven Armstrong). --- cdist/conf/type/__file/gencode-local | 115 +++---------------------- cdist/conf/type/__file/gencode-remote | 117 ++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 101 deletions(-) create mode 100755 cdist/conf/type/__file/gencode-remote diff --git a/cdist/conf/type/__file/gencode-local b/cdist/conf/type/__file/gencode-local index e7b819f5..2de20f49 100755 --- a/cdist/conf/type/__file/gencode-local +++ b/cdist/conf/type/__file/gencode-local @@ -22,43 +22,8 @@ destination="/$__object_id" state_should="$(cat "$__object/parameter/state")" type="$(cat "$__object/explorer/type")" -stat_file="$__object/explorer/stat" -fire_onchange='' -get_current_value() { - if [ -s "$stat_file" ]; then - _name="$1" - _value="$2" - case "$_value" in - [0-9]*) - _index=2 - ;; - *) - _index=3 - ;; - esac - awk '/'"$_name"':/ { print $'$_index' }' "$stat_file" - unset _name _value _index - fi -} - -set_group() { - remote_cmd="$remote_cmd chgrp '$1' '$destination';" - echo "chgrp '$1'" >> "$__messages_out" - fire_onchange=1 -} - -set_owner() { - remote_cmd="$remote_cmd chown '$1' '$destination';" - echo "chown '$1'" >> "$__messages_out" - fire_onchange=1 -} - -set_mode() { - remote_cmd="$remote_cmd chmod '$1' '$destination';" - echo "chmod '$1'" >> "$__messages_out" - fire_onchange=1 -} +[ "$state_should" = "exists" ] && [ "$type" = "file" ] && exit 0 # nothing to do if [ "$state_should" = "pre-exists" ]; then if [ -f "$__object/parameter/source" ]; then @@ -88,7 +53,6 @@ fi upload_file= create_file= -remote_cmd= if [ "$state_should" = "present" ] || [ "$state_should" = "exists" ]; then if [ ! -f "$__object/parameter/source" ]; then remote_stat="$(cat "$__object/explorer/stat")" @@ -125,10 +89,20 @@ if [ "$state_should" = "present" ] || [ "$state_should" = "exists" ]; then touch "$__object/files/set-attributes" # upload file to temp location - tempfile_template="${destination}.cdist.XXXXXXXXXX" + destination_upload="${destination}${__cdist_object_marker}" cat << DONE -destination_upload="\$($__remote_exec $__target_host "mktemp $tempfile_template")" +$__remote_exec $__target_host test -e "$destination_upload" && { + echo "Refusing to upload file to existing destination: $destination_upload" >&2 + exit 1 +} || { + # Put a towel in place. + $__remote_exec $__target_host "umask 077; touch \"$destination_upload\"" +} DONE + # Tell gencode-remote that it has to move our file to its + # final destination. + touch "$__object/files/file-uploaded" + if [ "$upload_file" ]; then echo upload >> "$__messages_out" # IPv6 fix @@ -139,69 +113,8 @@ DONE my_target_host="${__target_host}" fi cat << DONE -$__remote_copy "$source" "${my_target_host}:\$destination_upload" +$__remote_copy "$source" "${my_target_host}:${destination_upload}" DONE fi - remote_cmd="rm -rf \"$destination\"; mv \"\$destination_upload\" \"$destination\";" fi fi - -# Check to change any file attributes -case "$state_should" in - present|exists) - # Note: Mode - needs to happen last as a chown/chgrp can alter mode by - # clearing S_ISUID and S_ISGID bits (see chown(2)) - for attribute in group owner mode; do - if [ -f "$__object/parameter/$attribute" ]; then - value_should="$(cat "$__object/parameter/$attribute")" - - # format mode in four digits => same as stat returns - if [ "$attribute" = mode ]; then - # Convert to four-digit octal number (printf interprets - # strings with leading 0s as octal!) - value_should=$(printf '%04o' "0${value_should}") - fi - - value_is="$(get_current_value "$attribute" "$value_should")" - if [ -f "$__object/files/set-attributes" ] || [ "$value_should" != "$value_is" ]; then - "set_$attribute" "$value_should" - fi - fi - done - if [ -f "$__object/files/set-attributes" ]; then - # set-attributes is created if file is created or uploaded in gencode-local - fire_onchange=1 - fi - ;; - - absent) - if [ "$type" = "file" ]; then - #echo "rm -f '$destination'" - remote_cmd="$remote_cmd rm -f '$destination';" - echo remove >> "$__messages_out" - fire_onchange=1 - fi - ;; - - pre-exists) - : - ;; - - *) - echo "Unknown state: $state_should" >&2 - exit 1 - ;; -esac - -# Run any change to original file in 1 command as atomically as possible -if [ "$remote_cmd" != "" ]; then -cat << DONE -$__remote_exec $__target_host "$remote_cmd" -DONE -fi - -if [ -f "$__object/parameter/onchange" ]; then - if [ -n "$fire_onchange" ]; then - cat "$__object/parameter/onchange" - fi -fi diff --git a/cdist/conf/type/__file/gencode-remote b/cdist/conf/type/__file/gencode-remote new file mode 100755 index 00000000..f256b66e --- /dev/null +++ b/cdist/conf/type/__file/gencode-remote @@ -0,0 +1,117 @@ +#!/bin/sh -e +# +# 2011-2013 Nico Schottelius (nico-cdist at schottelius.org) +# 2013 Steven Armstrong (steven-cdist armstrong.cc) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +destination="/$__object_id" +state_should="$(cat "$__object/parameter/state")" +type="$(cat "$__object/explorer/type")" +stat_file="$__object/explorer/stat" +fire_onchange='' + +get_current_value() { + if [ -s "$stat_file" ]; then + _name="$1" + _value="$2" + case "$_value" in + [0-9]*) + _index=2 + ;; + *) + _index=3 + ;; + esac + awk '/'"$_name"':/ { print $'$_index' }' "$stat_file" + unset _name _value _index + fi +} + +set_group() { + echo "chgrp '$1' '$destination'" + echo "chgrp '$1'" >> "$__messages_out" + fire_onchange=1 +} + +set_owner() { + echo "chown '$1' '$destination'" + echo "chown '$1'" >> "$__messages_out" + fire_onchange=1 +} + +set_mode() { + echo "chmod '$1' '$destination'" + echo "chmod '$1'" >> "$__messages_out" + fire_onchange=1 +} + +case "$state_should" in + present|exists) + if [ -f "$__object/files/file-uploaded" ]; then + # move uploaded file into place + printf 'rm -rf "%s"\n' "$destination" + printf 'mv "%s" "%s"\n' "${destination}${__cdist_object_marker}" "$destination" + fi + # Note: Mode - needs to happen last as a chown/chgrp can alter mode by + # clearing S_ISUID and S_ISGID bits (see chown(2)) + for attribute in group owner mode; do + if [ -f "$__object/parameter/$attribute" ]; then + value_should="$(cat "$__object/parameter/$attribute")" + + # format mode in four digits => same as stat returns + if [ "$attribute" = mode ]; then + # Convert to four-digit octal number (printf interprets + # strings with leading 0s as octal!) + value_should=$(printf '%04o' "0${value_should}") + fi + + value_is="$(get_current_value "$attribute" "$value_should")" + if [ -f "$__object/files/set-attributes" ] || [ "$value_should" != "$value_is" ]; then + "set_$attribute" "$value_should" + fi + fi + done + if [ -f "$__object/files/set-attributes" ]; then + # set-attributes is created if file is created or uploaded in gencode-local + fire_onchange=1 + fi + ;; + + absent) + if [ "$type" = "file" ]; then + echo "rm -f '$destination'" + echo remove >> "$__messages_out" + fire_onchange=1 + fi + ;; + + pre-exists) + : + ;; + + *) + echo "Unknown state: $state_should" >&2 + exit 1 + ;; +esac + +if [ -f "$__object/parameter/onchange" ]; then + if [ -n "$fire_onchange" ]; then + cat "$__object/parameter/onchange" + fi +fi