Subject: bug#47381: Atomic (fast) install

From: Дилян Палаузов<[email-masked]>
Date: Thu, 25 Mar 2021 12:24:13 +0200

Hello,

I use automake with libtool .  The latter has “fast install” option. 
My config.status contains:

enable_fast_install='yes'
fast_install=$enable_fast_install

During “make install” it prints:

 /bin/sh ./libtool   --mode=install /usr/bin/install -c  
lib/libcyrus_min.la lib/libcyrus.la imap/libcyrus_imap.la
sieve/libcyrus_sieve.la '/usr/local/lib'  
libtool: install: /usr/bin/install -c lib/.libs/libcyrus_min.so.0.0.0
/usr/local/lib/libcyrus_min.so.0.0.0                                  
libtool: install: (cd /usr/local/lib && { ln -s -f
libcyrus_min.so.0.0.0 libcyrus_min.so.0 || { rm -f libcyrus_min.so.0 &&
ln -s libcyrus_min.so.0.0.0 libcyrus_min.so.0; }; })                  
libtool: install: (cd /usr/local/lib && { ln -s -f
libcyrus_min.so.0.0.0 libcyrus_min.so || { rm -f libcyrus_min.so && ln
-s libcyrus_min.so.0.0.0 libcyrus_min.so; }; })                       
libtool: install: /usr/bin/install -c lib/.libs/libcyrus_min.lai
/usr/local/lib/libcyrus_min.la                                        
libtool: install: /usr/bin/install -c lib/.libs/libcyrus.so.0.0.0
/usr/local/lib/libcyrus.so.0.0.0                                      
libtool: install: (cd /usr/local/lib && { ln -s -f libcyrus.so.0.0.0
libcyrus.so.0 || { rm -f libcyrus.so.0 && ln -s libcyrus.so.0.0.0
libcyrus.so.0; }; })   
libtool: install: (cd /usr/local/lib && { ln -s -f libcyrus.so.0.0.0
libcyrus.so || { rm -f libcyrus.so && ln -s libcyrus.so.0.0.0
libcyrus.so; }; })         
libtool: install: /usr/bin/install -c lib/.libs/libcyrus.lai
/usr/local/lib/libcyrus.la                                            
libtool: warning: relinking 'imap/libcyrus_imap.la'                   

for some shared libraries, but it does not print it for all shared
libraries.  It does not print it for libcyrus_min.so or libcyrus.so

Moreover (Auto)make install calls:

/bin/sh ./libtool --mode=install /usr/bin/install -c imap/httpd
'/usr/local/libexec'

which triggers these syscalls:

[pid 346601] stat("/usr/local/libexec/httpd", {st_mode=S_IFREG|0755,
st_size=3362912, ...}) = 0
[pid 346601] newfstatat(AT_FDCWD, "imap/.libs/httpd",
{st_mode=S_IFREG|0755, st_size=3362912, ...}, 0) = 0
[pid 346601] newfstatat(AT_FDCWD, "/usr/local/libexec/httpd",
{st_mode=S_IFREG|0755, st_size=3362912, ...}, AT_SYMLINK_NOFOLLOW) = 0
[pid 346601] unlink("/usr/local/libexec/httpd") = 0
[pid 346601] openat(AT_FDCWD, "imap/.libs/httpd", O_RDONLY) = 3
[pid 346601] fstat(3, {st_mode=S_IFREG|0755, st_size=3362912, ...}) = 0
[pid 346601] openat(AT_FDCWD, "/usr/local/libexec/httpd",
O_WRONLY|O_CREAT|O_EXCL, 0600) = 4
[pid 346601] fstat(4, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
[pid 346601] ioctl(4, BTRFS_IOC_CLONE or FICLONE, 3) = -1 EOPNOTSUPP
(Operation not supported)
[pid 346601] fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
[pid 346601] mmap(NULL, 139264, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb61f408000
[pid 346601] read(3,
"\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\2\0>\0\1\0\0\0\260\343A\0\0\0\0\0"...,
131072) = 131072
[pid 346601] write(4,
"\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\2\0>\0\1\0\0\0\260\343A\0\0\0\0\0"...,
131072) = 131072
[pid 346601] read(3,
"\377H\211\302H\213\205h\372\377\377\211\331H\211\306H\307\307\4\252\35
3\212\350t\0\6\0H\213E\340"..., 131072) = 131072
[pid 346601] write(4,
"\377H\211\302H\213\205h\372\377\377\211\331H\211\306H\307\307\4\252\35
3\212\350t\0\6\0H\213E\340"..., 131072) = 131072
...
[pid 346601] chmod("/usr/local/libexec/httpd", 0755) = 0
[pid 346601] lstat("/usr/local/libexec/httpd", {st_mode=S_IFREG|0755,
st_size=3362912, ...}) = 0

That said, the installation is not atomic.  The bytes destination file
is replaced byte by byte, rather than writing the result to a temporary
file and then calling rename(2), thus ensuring that the file is always
executable.

Cyrus IMAP is a server, consisting of several executable files
(daemons).  When an executable file notices, that its timestamp has
changed, the daemon terminates and is then started again (from the new
executable).  The problem is, that during this process, there is a
timegap, where the process-file has newer timestamp, but is not
executable yet, as `install` currently moves data to it.

As far the install executable is concerned, I raised the atomicity to
coreutils at debbugs.gnu.org/cgi/bugreport.cgi?bug=47380 .

• Please adjust Automake on “make install” to do (per default or with
an option) atomic install of files, even when libtool is involved: the
file is moved to a temporary destination first, and then rename(2)d, or
the source is moved to the destination, disappearing from the source,
or something like this.

• Why does “fast install” relink some libraries, but others not?

• Since ./libtool starts with 
  #! /bin/sh

do not call
  /bin/sh ./libtool --mode=install /usr/bin/install -c imap/httpd
'/usr/local/libexec'

but 
  ./libtool --mode=install /usr/bin/install -c imap/httpd
'/usr/local/libexec'

Greetings
  Дилян