Lines 404-413
module Util
Link Here
|
404 |
def replace_file(file, default_mode, &block) |
404 |
def replace_file(file, default_mode, &block) |
405 |
raise Puppet::DevError, "replace_file requires a block" unless block_given? |
405 |
raise Puppet::DevError, "replace_file requires a block" unless block_given? |
406 |
|
406 |
|
407 |
unless valid_symbolic_mode?(default_mode) |
407 |
if default_mode |
408 |
raise Puppet::DevError, "replace_file default_mode: #{default_mode} is invalid" |
408 |
unless valid_symbolic_mode?(default_mode) |
|
|
409 |
raise Puppet::DevError, "replace_file default_mode: #{default_mode} is invalid" |
410 |
end |
411 |
|
412 |
mode = symbolic_mode_to_int(normalize_symbolic_mode(default_mode)) |
409 |
end |
413 |
end |
410 |
mode = symbolic_mode_to_int(normalize_symbolic_mode(default_mode)) |
|
|
411 |
|
414 |
|
412 |
file = Pathname(file) |
415 |
file = Pathname(file) |
413 |
tempfile = Tempfile.new(file.basename.to_s, file.dirname.to_s) |
416 |
tempfile = Tempfile.new(file.basename.to_s, file.dirname.to_s) |
Lines 420-435
module Util
Link Here
|
420 |
# and specifically handle the platform, which has all sorts of magic. |
423 |
# and specifically handle the platform, which has all sorts of magic. |
421 |
# So, unlike Unix, we don't pre-prep security; we use the default "quite |
424 |
# So, unlike Unix, we don't pre-prep security; we use the default "quite |
422 |
# secure" tempfile permissions instead. Magic happens later. |
425 |
# secure" tempfile permissions instead. Magic happens later. |
423 |
unless Puppet.features.microsoft_windows? |
426 |
if !Puppet.features.microsoft_windows? |
424 |
# Grab the current file mode, and fall back to the defaults. |
427 |
# Grab the current file mode, and fall back to the defaults. |
425 |
stat = file.lstat rescue OpenStruct.new(:mode => mode, |
428 |
if file.exist? |
426 |
:uid => Process.euid, |
429 |
stat = file.lstat |
427 |
:gid => Process.egid) |
430 |
tempfile.chown(stat.uid, stat.gid) |
428 |
|
431 |
effective_mode = stat.mode |
429 |
# We only care about the bottom four slots, which make the real mode, |
432 |
else |
430 |
# and not the rest of the platform stat call fluff and stuff. |
433 |
effective_mode = mode |
431 |
tempfile.chmod(stat.mode & 07777) |
434 |
end |
432 |
tempfile.chown(stat.uid, stat.gid) |
435 |
|
|
|
436 |
if effective_mode |
437 |
# We only care about the bottom four slots, which make the real mode, |
438 |
# and not the rest of the platform stat call fluff and stuff. |
439 |
tempfile.chmod(effective_mode & 07777) |
440 |
end |
433 |
end |
441 |
end |
434 |
|
442 |
|
435 |
# OK, now allow the caller to write the content of the file. |
443 |
# OK, now allow the caller to write the content of the file. |
Lines 452-492
module Util
Link Here
|
452 |
tempfile.close |
460 |
tempfile.close |
453 |
|
461 |
|
454 |
if Puppet.features.microsoft_windows? |
462 |
if Puppet.features.microsoft_windows? |
455 |
# This will appropriately clone the file, but only if the file we are |
463 |
# Windows ReplaceFile needs a file to exist, so touch handles this |
456 |
# replacing exists. Which is kind of annoying; thanks Microsoft. |
464 |
if !file.exist? |
457 |
# |
465 |
FileUtils.touch(file.to_s) |
458 |
# So, to avoid getting into an infinite loop we will retry once if the |
466 |
if mode |
459 |
# file doesn't exist, but only the once... |
467 |
Puppet::Util::Windows::Security.set_mode(mode, file.to_s) |
460 |
have_retried = false |
|
|
461 |
|
462 |
begin |
463 |
# Yes, the arguments are reversed compared to the rename in the rest |
464 |
# of the world. |
465 |
Puppet::Util::Windows::File.replace_file(file, tempfile.path) |
466 |
rescue Puppet::Util::Windows::Error |
467 |
# This might race, but there are enough possible cases that there |
468 |
# isn't a good, solid "better" way to do this, and the next call |
469 |
# should fail in the same way anyhow. |
470 |
raise if have_retried or File.exist?(file) |
471 |
have_retried = true |
472 |
|
473 |
# OK, so, we can't replace a file that doesn't exist, so let us put |
474 |
# one in place and set the permissions. Then we can retry and the |
475 |
# magic makes this all work. |
476 |
# |
477 |
# This is the least-worst option for handling Windows, as far as we |
478 |
# can determine. |
479 |
File.open(file, 'a') do |fh| |
480 |
# this space deliberately left empty for auto-close behaviour, |
481 |
# append mode, and not actually changing any of the content. |
482 |
end |
468 |
end |
483 |
|
|
|
484 |
# Set the permissions to what we want. |
485 |
Puppet::Util::Windows::Security.set_mode(mode, file.to_s) |
486 |
|
487 |
# ...and finally retry the operation. |
488 |
retry |
489 |
end |
469 |
end |
|
|
470 |
# Yes, the arguments are reversed compared to the rename in the rest |
471 |
# of the world. |
472 |
Puppet::Util::Windows::File.replace_file(file, tempfile.path) |
473 |
|
490 |
else |
474 |
else |
491 |
File.rename(tempfile.path, file.to_s) |
475 |
File.rename(tempfile.path, file.to_s) |
492 |
end |
476 |
end |