Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion lib/fpm/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
require "fpm/version"
require "fpm/util"
require "clamp"
require "ostruct"

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this to get rid of a Ruby warning about a future change: "warning: ostruct was loaded from the standard library, but will no longer be part of the default gems starting from Ruby 3.5.0."

require "fpm"
require "tmpdir" # for Dir.tmpdir

Expand Down
1 change: 0 additions & 1 deletion lib/fpm/package.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
require "pathname" # stdlib
require "find"
require "tmpdir" # stdlib
require "ostruct"
require "backports/latest"
require "socket" # stdlib, for Socket.gethostname
require "shellwords" # stdlib, for Shellwords.escape
Expand Down
4 changes: 0 additions & 4 deletions lib/fpm/package/dir.rb
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,6 @@ def copy(source, destination)
else
# Otherwise try copying the file.
begin
logger.debug("Linking", :source => source, :destination => destination)
File.link(source, destination)
rescue Errno::ENOENT, Errno::EXDEV, Errno::EPERM
# Hardlink attempt failed, copy it instead
logger.debug("Copying", :source => source, :destination => destination)
copy_entry(source, destination)
rescue Errno::EEXIST
Expand Down
21 changes: 17 additions & 4 deletions lib/fpm/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,15 @@ def copy_metadata(source, destination)


def copy_entry(src, dst, preserve=false, remove_destination=false)
case File.ftype(src)
st = File.lstat(src)

filetype = if st.ftype == "file" && st.nlink > 1
"hardlink"
else
st.ftype
end

case filetype
when 'fifo'
if File.respond_to?(:mkfifo)
File.mkfifo(dst)
Expand All @@ -350,18 +358,23 @@ def copy_entry(src, dst, preserve=false, remove_destination=false)
raise UnsupportedSpecialFile.new("File is device which fpm doesn't know how to copy (#{File.ftype(src)}): #{src}")
when 'directory'
FileUtils.mkdir(dst) unless File.exist? dst
else
# if the file with the same dev and inode has been copied already -
when 'hardlink'
# Handle hardlinks
# if the file with the same dev and inode has been copied already.
# hard link it's copy to `dst`, otherwise make an actual copy
st = File.lstat(src)
known_entry = copied_entries[[st.dev, st.ino]]
if known_entry
FileUtils.ln(known_entry, dst)
logger.debug("Copying hardlink", :src => src, :dst => dst, :link => known_entry)
else
FileUtils.copy_entry(src, dst, preserve, false,
remove_destination)
copied_entries[[st.dev, st.ino]] = dst
end
else
# Normal file, just copy it.
FileUtils.copy_entry(src, dst, preserve, false,
remove_destination)
end # else...
end # def copy_entry

Expand Down
44 changes: 44 additions & 0 deletions spec/fpm/package/dir_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
require "fpm" # local
require "fpm/package/dir" # local
require "stud/temporary"
require "insist/assert"

if RUBY_VERSION =~ /^1\.8/
# The following method copied from ruby 1.9.3
Expand Down Expand Up @@ -126,6 +127,49 @@ def self.uuid
subject.output(output)
insist { File }.exist?(File.join(output, "foo", "a", "a=b"))
end

it "should create two normal files when one normal file is copied to two different locations" do
# For issue #2102
# With the following: fpm -s dir ... pathA=/location1 pathA=location2
# The above command was copying pathA to both locations but hardlinking them instead of creating normal files.

foo = File.join(tmpdir, "foo")
File.write(foo, "hello world")

paths = [ "/opt/example/foo", "/usr/share/example/foo" ]
paths.each do |path|
subject.input("#{foo}=#{path}")
end

subject.output(output)

outfiles = paths.collect { |path| File.join(output, path) }

expect(outfiles).to all(satisfy("have link count == 1") { |path| File.lstat(path).nlink == 1 })
end

end

context "hardlinks" do
it "should create hardlinks when inputs are hardlinks (within the context of the target package)" do
# For issue #2102
# With the following: fpm -s dir ... pathA=/location1 pathA=location2
# The above command was copying pathA to both locations but hardlinking them instead of creating normal files.

foo = File.join(tmpdir, "foo")
bar = File.join(tmpdir, "bar")
File.write(foo, "hello world")
File.link(foo, bar)

subject.attributes[:chdir] = tmpdir
subject.input(".")

subject.output(output)

outfiles = ["foo", "bar"].collect { |path| File.join(output, path) }

expect(outfiles).to all(satisfy("have link count == 2") { |path| File.stat(path).nlink == 2 })
end
end

context "SYMLINKS." do
Expand Down
Loading