[nycbug-talk] anyone know why install functions this way?

Matthew Story matt at tablethotels.com
Mon Dec 12 11:58:56 EST 2011


For anyone interested, there does actually seem to be a valid reason for this behavior, the reason is that you can `install' an existing directory with owner/group flags set.  This means that if you are installing to an existing directory, and it fails ... unlink would remove the directory and any contents ... hardly desirable.  install -d does the following:

1. byte-wise walk path, stopping at each '/' character
   +-> for each sub-path
   +----> stat sub-path
   +--------> doesn't exist (ENOENT)
   +-------------> mkdir with file mode 755 || exit error
   +--------> exists but is not a directory && exit error
   
2. if gid or uid is specified and not equal to effective uid/gid:
   +-> chown leaf directory || warn
3. chmod leaf directory || warn

Might be interesting to make this a little more robust for "new" directories, and to exit non-0 even for existing dirs, while preserving content.  Will take this up on bugs list for FreeBSD.  Thanks for playing along all.

relevant C code below (via: http://svn.freebsd.org/base/stable/8/usr.bin/xinstall/xinstall.c ... web viewable here: http://svnweb.freebsd.org/base/stable/8/usr.bin/xinstall/xinstall.c)

/*
 * install_dir --
 *      build directory hierarchy
 */
void
install_dir(char *path)
{
        char *p; 
        struct stat sb; 
        int ch; 

        for (p = path;; ++p)
                if (!*p || (p != path && *p  == '/')) {
                        ch = *p; 
                        *p = '\0';
                        if (stat(path, &sb)) {
                                if (errno != ENOENT || mkdir(path, 0755) < 0) {
                                        err(EX_OSERR, "mkdir %s", path);
                                        /* NOTREACHED */
                                } else if (verbose)
                                        (void)printf("install: mkdir %s\n",
                                                     path);
                        } else if (!S_ISDIR(sb.st_mode))
                                errx(EX_OSERR, "%s exists but is not a directory", path);
                        if (!(*p = ch))
                                break;
                }   

        if ((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid))
                warn("chown %u:%u %s", uid, gid, path);
        if (chmod(path, mode))
                warn("chmod %o %s", mode, path);
}

On Dec 12, 2011, at 9:55 AM, Matthew Story wrote:

> one correction below ...
> 
> On Dec 12, 2011, at 9:41 AM, Matthew Story wrote:
> 
>> [snip...]
>> A couple of work-arounds I'm using for now below (if anyone is interested) ...
>> 
>> # assume success until failure method ...
>> shout() { echo "$0: $*" >&2; }
>> barf() { shout "$*"; exit 111; }
>> cleanup() {
>>   rc="$?"
>>   [ ! -z "$working" -a -e "$working" ] && rm -r "$working"
>>   exit "$rc"
>> }
>> dir_install() {
>>   eval working=\"\$$#\"
>>   trap cleanup INT TERM EXIT
>     install "$@" 2>&1 | {
>>       read err 
>>       [ -z "$err" ] || barf "cannot install dir: $working"
>>   } || exit $?
>>   trap "" INT TERM EXIT
>> }
>> dir_install -o foo -d "some.dir"
>> 
>> # test for failure up front, with a file
>> test_file="test_install-d.$$.`hostname`.`date +%s`"
>> install -o "user" -g "group" "$test_file"|| rc=$?
>> [ -e "$test_file" ] && rm "$test_file"
>> [ 0 -ne "$rc" ] && exit "$rc"
>> install -o "user" -g "group" -d "$dir" || exit $?
>> 
>> 
>> On Dec 9, 2011, at 8:08 PM, Matthew Story wrote:
>> 
>>> Know this isn't a bug list or anything like that, but figured I'd throw this out there ... the only thing I found on the interweb was regarding RPMs on CentOS ... so this is apparently not even a BSDism, but it makes writing my installer scripts vastly less reliable.  Lots more below, but the gist is this:
>>> 
>>> install -o root -d foo/
>>> 
>>> Will create the directory with my user and effective group id, and my umask ... and yield an error message ... but still exit 0 (success).  At the very least I would expect the directory to linger with these permissions and exit non-zero (failure) ... this is also not consistant with the behavior of files.
>>> 
>>> Anyway ... anyone know a historical reason for this, or have a good hack around this ... getting patches into section 1 to trickle down into my world takes a while, assuming the behavior is even a bug ...
>>> 
>>> ------------------------lots of copy-paste below----------------------------------
>>> 
>>> [matt ~]$ mkdir install-showcase
>>> [matt ~]$ cd install-showcase/
>>> [matt ~/install-showcase]$ who am i
>>> matt             14       Dec  9 19:52 
>>> [matt ~/install-showcase]$ install -d ./test.dir; echo $? # install a directory as me, success!
>>> 0
>>> [matt ~/install-showcase]$ ls -d ./test.dir; echo $? # see, success!
>>> ./test.dir
>>> 0
>>> [matt ~/install-showcase]$ install -o root -d ./test.dir.1; echo $? # install a directory as root, success?!                                                                   
>>> install: chown 0:4294967295 ./test.dir.1: Operation not permitted
>>> 0
>>> [matt ~/install-showcase]$ ls -d ./test.dir.1 # see, success!
>>> ./test.dir.1
>>> [matt ~/install-showcase]$ ls -d ./test.dir.1; echo $? # see, success!                                                                                                         
>>> ./test.dir.1
>>> 0
>>> [matt ~/install-showcase]$ install -o doesnotexist -d ./test.dir.2; echo $? # install directory as a user that doesn't exist, failure!
>>> install: doesnotexist: Invalid argument
>>> 67
>>> [matt ~/install-showcase]$ touch filetobeinstalled; ls filetobeinstalled; echo $? # but does it work with files?                                                               
>>> filetobeinstalled
>>> 0
>>> [matt ~/install-showcase]$ install filetobeinstalled test.file; echo $? # install a file as me, success!                                                                       
>>> 0
>>> [matt ~/install-showcase]$ ls filetobeinstalled; echo $?
>>> filetobeinstalled
>>> 0
>>> [matt ~/install-showcase]$ install -o root filetobeinstalled test.file.1; echo $? # install a file as root, success?                                                           
>>> install: test.file.1: chown/chgrp: Operation not permitted
>>> 71
>>> [matt ~/install-showcase]$ ls test.file.1; echo $? # not even a lingering file                                                                                                 
>>> ls: test.file.1: No such file or directory
>>> 1
>>> [matt ~/install-showcase]$ install -S -o root -d test.dir.2; echo $? # how about a directory in safe mode?                                                                     
>>> install: chown 0:4294967295 test.dir.2: Operation not permitted
>>> 0
>>> [matt ~/install-showcase]$ ls -d test.dir.2; echo $? # yup ... safe mode too ... :(
>>> test.dir.2
>>> 0
>>> [matt ~/install-showcase]$ install -S -o root -d test.dir.2; echo $? # how about a directory in safe mode ... with an existing target
>>> install: chown 0:4294967295 test.dir.2: Operation not permitted
>>> 0
>>> [matt ~/install-showcase]$ ls -ld test.dir.2; echo $? # yup ... safe mode too ... :(
>>> drwxr-xr-x  2 matt  matt  2 Dec  9 20:00 test.dir.2
>>> 0
>>> [matt ~/install-showcase]$ install -S -o root -d test.dir.2; echo $? # how about a directory in safe mode ... with an existing target
>>> install: chown 0:4294967295 test.dir.2: Operation not permitted
>>> 0
>>> _______________________________________________
>>> talk mailing list
>>> talk at lists.nycbug.org
>>> http://lists.nycbug.org/mailman/listinfo/talk
>>> 
>> 
>> 
> 
> 




More information about the talk mailing list