| GCC Code Coverage Report | |||||||||||||||||||||
        
  | 
    |||||||||||||||||||||
| Line | Branch | Exec | Source | 
1  | 
    /* $OpenBSD: xinstall.c,v 1.66 2017/08/21 21:41:13 deraadt Exp $ */  | 
    ||
2  | 
    /* $NetBSD: xinstall.c,v 1.9 1995/12/20 10:25:17 jonathan Exp $ */  | 
    ||
3  | 
    |||
4  | 
    /*  | 
    ||
5  | 
    * Copyright (c) 1987, 1993  | 
    ||
6  | 
    * The Regents of the University of California. All rights reserved.  | 
    ||
7  | 
    *  | 
    ||
8  | 
    * Redistribution and use in source and binary forms, with or without  | 
    ||
9  | 
    * modification, are permitted provided that the following conditions  | 
    ||
10  | 
    * are met:  | 
    ||
11  | 
    * 1. Redistributions of source code must retain the above copyright  | 
    ||
12  | 
    * notice, this list of conditions and the following disclaimer.  | 
    ||
13  | 
    * 2. Redistributions in binary form must reproduce the above copyright  | 
    ||
14  | 
    * notice, this list of conditions and the following disclaimer in the  | 
    ||
15  | 
    * documentation and/or other materials provided with the distribution.  | 
    ||
16  | 
    * 3. Neither the name of the University nor the names of its contributors  | 
    ||
17  | 
    * may be used to endorse or promote products derived from this software  | 
    ||
18  | 
    * without specific prior written permission.  | 
    ||
19  | 
    *  | 
    ||
20  | 
    * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND  | 
    ||
21  | 
    * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  | 
    ||
22  | 
    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  | 
    ||
23  | 
    * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE  | 
    ||
24  | 
    * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL  | 
    ||
25  | 
    * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS  | 
    ||
26  | 
    * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)  | 
    ||
27  | 
    * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT  | 
    ||
28  | 
    * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY  | 
    ||
29  | 
    * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  | 
    ||
30  | 
    * SUCH DAMAGE.  | 
    ||
31  | 
    */  | 
    ||
32  | 
    |||
33  | 
    #include <sys/param.h> /* MAXBSIZE */  | 
    ||
34  | 
    #include <sys/wait.h>  | 
    ||
35  | 
    #include <sys/mman.h>  | 
    ||
36  | 
    #include <sys/stat.h>  | 
    ||
37  | 
    |||
38  | 
    #include <ctype.h>  | 
    ||
39  | 
    #include <err.h>  | 
    ||
40  | 
    #include <errno.h>  | 
    ||
41  | 
    #include <fcntl.h>  | 
    ||
42  | 
    #include <grp.h>  | 
    ||
43  | 
    #include <paths.h>  | 
    ||
44  | 
    #include <pwd.h>  | 
    ||
45  | 
    #include <stdio.h>  | 
    ||
46  | 
    #include <stdlib.h>  | 
    ||
47  | 
    #include <string.h>  | 
    ||
48  | 
    #include <unistd.h>  | 
    ||
49  | 
    #include <limits.h>  | 
    ||
50  | 
    #include <utime.h>  | 
    ||
51  | 
    #include <libgen.h>  | 
    ||
52  | 
    |||
53  | 
    #include "pathnames.h"  | 
    ||
54  | 
    |||
55  | 
    #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))  | 
    ||
56  | 
    |||
57  | 
    #define DIRECTORY 0x01 /* Tell install it's a directory. */  | 
    ||
58  | 
    #define SETFLAGS 0x02 /* Tell install to set flags. */  | 
    ||
59  | 
    #define USEFSYNC 0x04 /* Tell install to use fsync(2). */  | 
    ||
60  | 
    #define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)  | 
    ||
61  | 
    #define BACKUP_SUFFIX ".old"  | 
    ||
62  | 
    |||
63  | 
    struct passwd *pp;  | 
    ||
64  | 
    struct group *gp;  | 
    ||
65  | 
    int dobackup, docompare, dodest, dodir, dopreserve, dostrip, safecopy;  | 
    ||
66  | 
    int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;  | 
    ||
67  | 
    char pathbuf[PATH_MAX], tempfile[PATH_MAX];  | 
    ||
68  | 
    char *suffix = BACKUP_SUFFIX;  | 
    ||
69  | 
    uid_t uid;  | 
    ||
70  | 
    gid_t gid;  | 
    ||
71  | 
    |||
72  | 
    void copy(int, char *, int, char *, off_t, int);  | 
    ||
73  | 
    int compare(int, const char *, off_t, int, const char *, off_t);  | 
    ||
74  | 
    void install(char *, char *, u_long, u_int);  | 
    ||
75  | 
    void install_dir(char *, int);  | 
    ||
76  | 
    void strip(char *);  | 
    ||
77  | 
    void usage(void);  | 
    ||
78  | 
    int create_newfile(char *, struct stat *);  | 
    ||
79  | 
    int create_tempfile(char *, char *, size_t);  | 
    ||
80  | 
    int file_write(int, char *, size_t, int *, int *, int);  | 
    ||
81  | 
    void file_flush(int, int);  | 
    ||
82  | 
    |||
83  | 
    int  | 
    ||
84  | 
    main(int argc, char *argv[])  | 
    ||
85  | 
    { | 
    ||
86  | 
    39406  | 
    struct stat from_sb, to_sb;  | 
    |
87  | 
    void *set;  | 
    ||
88  | 
    19703  | 
    u_int32_t fset;  | 
    |
89  | 
    u_int iflags;  | 
    ||
90  | 
    int ch, no_target;  | 
    ||
91  | 
    19703  | 
    char *flags, *to_name, *group = NULL, *owner = NULL;  | 
    |
92  | 
    |||
93  | 
    iflags = 0;  | 
    ||
94  | 
    ✓✓ | 128679  | 
    while ((ch = getopt(argc, argv, "B:bCcDdFf:g:m:o:pSs")) != -1)  | 
    
95  | 
    ✓✓✗✓ ✗✗✓✓ ✓✗✓✓ ✓✓✗  | 
    160111  | 
    		switch(ch) { | 
    
96  | 
    case 'C':  | 
    ||
97  | 
    1112  | 
    docompare = 1;  | 
    |
98  | 
    1112  | 
    break;  | 
    |
99  | 
    case 'B':  | 
    ||
100  | 
    suffix = optarg;  | 
    ||
101  | 
    /* fall through; -B implies -b */  | 
    ||
102  | 
    case 'b':  | 
    ||
103  | 
    2  | 
    dobackup = 1;  | 
    |
104  | 
    2  | 
    break;  | 
    |
105  | 
    case 'c':  | 
    ||
106  | 
    /* For backwards compatibility. */  | 
    ||
107  | 
    break;  | 
    ||
108  | 
    case 'F':  | 
    ||
109  | 
    iflags |= USEFSYNC;  | 
    ||
110  | 
    break;  | 
    ||
111  | 
    case 'f':  | 
    ||
112  | 
    flags = optarg;  | 
    ||
113  | 
    if (strtofflags(&flags, &fset, NULL))  | 
    ||
114  | 
    errx(1, "%s: invalid flag", flags);  | 
    ||
115  | 
    iflags |= SETFLAGS;  | 
    ||
116  | 
    break;  | 
    ||
117  | 
    case 'g':  | 
    ||
118  | 
    19646  | 
    group = optarg;  | 
    |
119  | 
    19646  | 
    break;  | 
    |
120  | 
    case 'm':  | 
    ||
121  | 
    ✗✓ | 19684  | 
    if (!(set = setmode(optarg)))  | 
    
122  | 
    errx(1, "%s: invalid file mode", optarg);  | 
    ||
123  | 
    19684  | 
    mode = getmode(set, 0);  | 
    |
124  | 
    19684  | 
    free(set);  | 
    |
125  | 
    19684  | 
    break;  | 
    |
126  | 
    case 'o':  | 
    ||
127  | 
    19674  | 
    owner = optarg;  | 
    |
128  | 
    19674  | 
    break;  | 
    |
129  | 
    case 'p':  | 
    ||
130  | 
    docompare = dopreserve = 1;  | 
    ||
131  | 
    break;  | 
    ||
132  | 
    case 'S':  | 
    ||
133  | 
    1360  | 
    safecopy = 1;  | 
    |
134  | 
    1360  | 
    break;  | 
    |
135  | 
    case 's':  | 
    ||
136  | 
    1082  | 
    dostrip = 1;  | 
    |
137  | 
    1082  | 
    break;  | 
    |
138  | 
    case 'D':  | 
    ||
139  | 
    8169  | 
    dodest = 1;  | 
    |
140  | 
    8169  | 
    break;  | 
    |
141  | 
    case 'd':  | 
    ||
142  | 
    109  | 
    dodir = 1;  | 
    |
143  | 
    109  | 
    break;  | 
    |
144  | 
    case '?':  | 
    ||
145  | 
    default:  | 
    ||
146  | 
    usage();  | 
    ||
147  | 
    }  | 
    ||
148  | 
    19703  | 
    argc -= optind;  | 
    |
149  | 
    19703  | 
    argv += optind;  | 
    |
150  | 
    |||
151  | 
    /* some options make no sense when creating directories */  | 
    ||
152  | 
    ✗✓ | 19703  | 
    if ((safecopy || docompare || dostrip) && dodir)  | 
    
153  | 
    usage();  | 
    ||
154  | 
    |||
155  | 
    /* must have at least two arguments, except when creating directories */  | 
    ||
156  | 
    ✗✓ | 19703  | 
    if (argc < 2 && !dodir)  | 
    
157  | 
    usage();  | 
    ||
158  | 
    |||
159  | 
    /* need to make a temp copy so we can compare stripped version */  | 
    ||
160  | 
    ✗✓ | 19703  | 
    if (docompare && dostrip)  | 
    
161  | 
    safecopy = 1;  | 
    ||
162  | 
    |||
163  | 
    /* get group and owner id's */  | 
    ||
164  | 
    ✓✓✓✓ ✗✓  | 
    39352  | 
    if (group && !(gp = getgrnam(group)) && !isdigit((unsigned char)*group))  | 
    
165  | 
    errx(1, "unknown group %s", group);  | 
    ||
166  | 
    ✓✓✓✓ | 
    78698  | 
    gid = (group) ? ((gp) ? gp->gr_gid : (gid_t)strtoul(group, NULL, 10)) : (gid_t)-1;  | 
    
167  | 
    ✓✓✓✓ ✗✓  | 
    39380  | 
    if (owner && !(pp = getpwnam(owner)) && !isdigit((unsigned char)*owner))  | 
    
168  | 
    errx(1, "unknown user %s", owner);  | 
    ||
169  | 
    ✓✓✓✓ | 
    78754  | 
    uid = (owner) ? ((pp) ? pp->pw_uid : (uid_t)strtoul(owner, NULL, 10)) : (uid_t)-1;  | 
    
170  | 
    |||
171  | 
    ✓✓ | 19703  | 
    	if (dodir) { | 
    
172  | 
    ✓✓ | 327  | 
    for (; *argv != NULL; ++argv)  | 
    
173  | 
    109  | 
    install_dir(*argv, mode);  | 
    |
174  | 
    exit(0);  | 
    ||
175  | 
    /* NOTREACHED */  | 
    ||
176  | 
    }  | 
    ||
177  | 
    |||
178  | 
    ✓✓ | 19594  | 
    	if (dodest) { | 
    
179  | 
    8169  | 
    char *dest = dirname(argv[argc - 1]);  | 
    |
180  | 
    ✗✓ | 8169  | 
    if (dest == NULL)  | 
    
181  | 
    errx(1, "cannot determine dirname");  | 
    ||
182  | 
    /*  | 
    ||
183  | 
    * When -D is passed, do not chmod the directory with the mode set for  | 
    ||
184  | 
    * the target file. If more restrictive permissions are required then  | 
    ||
185  | 
    * '-d -m' ought to be used instead.  | 
    ||
186  | 
    */  | 
    ||
187  | 
    8169  | 
    install_dir(dest, 0755);  | 
    |
188  | 
    8169  | 
    }  | 
    |
189  | 
    |||
190  | 
    19594  | 
    no_target = stat(to_name = argv[argc - 1], &to_sb);  | 
    |
191  | 
    ✓✓✓✓ | 
    39174  | 
    	if (!no_target && S_ISDIR(to_sb.st_mode)) { | 
    
192  | 
    ✓✓ | 4467  | 
    for (; *argv != to_name; ++argv)  | 
    
193  | 
    1596  | 
    install(*argv, to_name, fset, iflags | DIRECTORY);  | 
    |
194  | 
    exit(0);  | 
    ||
195  | 
    /* NOTREACHED */  | 
    ||
196  | 
    }  | 
    ||
197  | 
    |||
198  | 
    /* can't do file1 file2 directory/file */  | 
    ||
199  | 
    ✗✓ | 18319  | 
    if (argc != 2)  | 
    
200  | 
    errx(1, "Target: %s", argv[argc-1]);  | 
    ||
201  | 
    |||
202  | 
    ✓✓ | 18319  | 
    	if (!no_target) { | 
    
203  | 
    ✓✓ | 18305  | 
    if (stat(*argv, &from_sb))  | 
    
204  | 
    err(1, "%s", *argv);  | 
    ||
205  | 
    ✗✓ | 18304  | 
    if (!S_ISREG(to_sb.st_mode))  | 
    
206  | 
    errc(1, EFTYPE, "%s", to_name);  | 
    ||
207  | 
    ✓✓✗✓ | 
    18333  | 
    if (to_sb.st_dev == from_sb.st_dev &&  | 
    
208  | 
    29  | 
    to_sb.st_ino == from_sb.st_ino)  | 
    |
209  | 
    errx(1, "%s and %s are the same file", *argv, to_name);  | 
    ||
210  | 
    }  | 
    ||
211  | 
    install(*argv, to_name, fset, iflags);  | 
    ||
212  | 
    exit(0);  | 
    ||
213  | 
    /* NOTREACHED */  | 
    ||
214  | 
    }  | 
    ||
215  | 
    |||
216  | 
    /*  | 
    ||
217  | 
    * install --  | 
    ||
218  | 
    * build a path name and install the file  | 
    ||
219  | 
    */  | 
    ||
220  | 
    void  | 
    ||
221  | 
    install(char *from_name, char *to_name, u_long fset, u_int flags)  | 
    ||
222  | 
    { | 
    ||
223  | 
    39828  | 
    struct stat from_sb, to_sb;  | 
    |
224  | 
    19914  | 
    struct timespec ts[2];  | 
    |
225  | 
    int devnull, from_fd, to_fd, serrno, files_match = 0;  | 
    ||
226  | 
    char *p;  | 
    ||
227  | 
    |||
228  | 
    19914  | 
    (void)memset((void *)&from_sb, 0, sizeof(from_sb));  | 
    |
229  | 
    19914  | 
    (void)memset((void *)&to_sb, 0, sizeof(to_sb));  | 
    |
230  | 
    |||
231  | 
    /* If try to install NULL file to a directory, fails. */  | 
    ||
232  | 
    ✓✓✓✓ | 
    38232  | 
    	if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) { | 
    
233  | 
    ✗✓ | 19911  | 
    if (stat(from_name, &from_sb))  | 
    
234  | 
    err(1, "%s", from_name);  | 
    ||
235  | 
    ✗✓ | 19911  | 
    if (!S_ISREG(from_sb.st_mode))  | 
    
236  | 
    errc(1, EFTYPE, "%s", from_name);  | 
    ||
237  | 
    /* Build the target path. */  | 
    ||
238  | 
    ✓✓ | 19911  | 
    		if (flags & DIRECTORY) { | 
    
239  | 
    1596  | 
    (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",  | 
    |
240  | 
    to_name,  | 
    ||
241  | 
    ✓✓ | 4788  | 
    (p = strrchr(from_name, '/')) ? ++p : from_name);  | 
    
242  | 
    to_name = pathbuf;  | 
    ||
243  | 
    1596  | 
    }  | 
    |
244  | 
    devnull = 0;  | 
    ||
245  | 
    19911  | 
    	} else { | 
    |
246  | 
    devnull = 1;  | 
    ||
247  | 
    }  | 
    ||
248  | 
    |||
249  | 
    ✓✓ | 19914  | 
    	if (stat(to_name, &to_sb) == 0) { | 
    
250  | 
    /* Only compare against regular files. */  | 
    ||
251  | 
    ✓✓✗✓ | 
    21046  | 
    		if (docompare && !S_ISREG(to_sb.st_mode)) { | 
    
252  | 
    docompare = 0;  | 
    ||
253  | 
    warnc(EFTYPE, "%s", to_name);  | 
    ||
254  | 
    }  | 
    ||
255  | 
    ✗✓ | 16  | 
    	} else if (docompare) { | 
    
256  | 
    /* File does not exist so silently ignore compare flag. */  | 
    ||
257  | 
    docompare = 0;  | 
    ||
258  | 
    }  | 
    ||
259  | 
    |||
260  | 
    ✓✓ | 19914  | 
    	if (!devnull) { | 
    
261  | 
    ✗✓ | 19911  | 
    if ((from_fd = open(from_name, O_RDONLY, 0)) < 0)  | 
    
262  | 
    err(1, "%s", from_name);  | 
    ||
263  | 
    }  | 
    ||
264  | 
    |||
265  | 
    ✓✓ | 19914  | 
    	if (safecopy) { | 
    
266  | 
    1383  | 
    to_fd = create_tempfile(to_name, tempfile, sizeof(tempfile));  | 
    |
267  | 
    ✗✓ | 1383  | 
    if (to_fd < 0)  | 
    
268  | 
    err(1, "%s", tempfile);  | 
    ||
269  | 
    ✓✓ | 18531  | 
    	} else if (docompare && !dostrip) { | 
    
270  | 
    ✗✓ | 1148  | 
    if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)  | 
    
271  | 
    err(1, "%s", to_name);  | 
    ||
272  | 
    	} else { | 
    ||
273  | 
    ✗✓ | 17383  | 
    if ((to_fd = create_newfile(to_name, &to_sb)) < 0)  | 
    
274  | 
    err(1, "%s", to_name);  | 
    ||
275  | 
    }  | 
    ||
276  | 
    |||
277  | 
    ✓✓ | 19914  | 
    	if (!devnull) { | 
    
278  | 
    ✓✓ | 19911  | 
    		if (docompare && !safecopy) { | 
    
279  | 
    1148  | 
    files_match = !(compare(from_fd, from_name,  | 
    |
280  | 
    1148  | 
    from_sb.st_size, to_fd,  | 
    |
281  | 
    1148  | 
    to_name, to_sb.st_size));  | 
    |
282  | 
    |||
283  | 
    /* Truncate "to" file for copy unless we match */  | 
    ||
284  | 
    ✗✓ | 1148  | 
    			if (!files_match) { | 
    
285  | 
    (void)close(to_fd);  | 
    ||
286  | 
    if ((to_fd = create_newfile(to_name, &to_sb)) < 0)  | 
    ||
287  | 
    err(1, "%s", to_name);  | 
    ||
288  | 
    }  | 
    ||
289  | 
    }  | 
    ||
290  | 
    ✓✓ | 19911  | 
    if (!files_match)  | 
    
291  | 
    18763  | 
    copy(from_fd, from_name, to_fd,  | 
    |
292  | 
    18763  | 
    safecopy ? tempfile : to_name, from_sb.st_size,  | 
    |
293  | 
    18763  | 
    ((off_t)from_sb.st_blocks * S_BLKSIZE < from_sb.st_size));  | 
    |
294  | 
    }  | 
    ||
295  | 
    |||
296  | 
    ✓✓ | 19914  | 
    	if (dostrip) { | 
    
297  | 
    1081  | 
    strip(safecopy ? tempfile : to_name);  | 
    |
298  | 
    |||
299  | 
    /*  | 
    ||
300  | 
    * Re-open our fd on the target, in case we used a strip  | 
    ||
301  | 
    * that does not work in-place -- like gnu binutils strip.  | 
    ||
302  | 
    */  | 
    ||
303  | 
    1081  | 
    close(to_fd);  | 
    |
304  | 
    ✗✓ | 2162  | 
    if ((to_fd = open(safecopy ? tempfile : to_name, O_RDONLY,  | 
    
305  | 
    1081  | 
    0)) < 0)  | 
    |
306  | 
    err(1, "stripping %s", to_name);  | 
    ||
307  | 
    }  | 
    ||
308  | 
    |||
309  | 
    /*  | 
    ||
310  | 
    * Compare the (possibly stripped) temp file to the target.  | 
    ||
311  | 
    */  | 
    ||
312  | 
    ✗✓ | 19914  | 
    	if (safecopy && docompare) { | 
    
313  | 
    int temp_fd = to_fd;  | 
    ||
314  | 
    struct stat temp_sb;  | 
    ||
315  | 
    |||
316  | 
    /* Re-open to_fd using the real target name. */  | 
    ||
317  | 
    if ((to_fd = open(to_name, O_RDONLY, 0)) < 0)  | 
    ||
318  | 
    err(1, "%s", to_name);  | 
    ||
319  | 
    |||
320  | 
    		if (fstat(temp_fd, &temp_sb)) { | 
    ||
321  | 
    serrno = errno;  | 
    ||
322  | 
    (void)unlink(tempfile);  | 
    ||
323  | 
    errc(1, serrno, "%s", tempfile);  | 
    ||
324  | 
    }  | 
    ||
325  | 
    |||
326  | 
    if (compare(temp_fd, tempfile, temp_sb.st_size, to_fd,  | 
    ||
327  | 
    			    to_name, to_sb.st_size) == 0) { | 
    ||
328  | 
    /*  | 
    ||
329  | 
    * If target has more than one link we need to  | 
    ||
330  | 
    * replace it in order to snap the extra links.  | 
    ||
331  | 
    * Need to preserve target file times, though.  | 
    ||
332  | 
    */  | 
    ||
333  | 
    			if (to_sb.st_nlink != 1) { | 
    ||
334  | 
    ts[0] = to_sb.st_atim;  | 
    ||
335  | 
    ts[1] = to_sb.st_mtim;  | 
    ||
336  | 
    futimens(temp_fd, ts);  | 
    ||
337  | 
    			} else { | 
    ||
338  | 
    files_match = 1;  | 
    ||
339  | 
    (void)unlink(tempfile);  | 
    ||
340  | 
    }  | 
    ||
341  | 
    }  | 
    ||
342  | 
    (void)close(to_fd);  | 
    ||
343  | 
    to_fd = temp_fd;  | 
    ||
344  | 
    }  | 
    ||
345  | 
    |||
346  | 
    /*  | 
    ||
347  | 
    * Preserve the timestamp of the source file if necessary.  | 
    ||
348  | 
    */  | 
    ||
349  | 
    ✗✓ | 19914  | 
    	if (dopreserve && !files_match) { | 
    
350  | 
    ts[0] = from_sb.st_atim;  | 
    ||
351  | 
    ts[1] = from_sb.st_mtim;  | 
    ||
352  | 
    futimens(to_fd, ts);  | 
    ||
353  | 
    }  | 
    ||
354  | 
    |||
355  | 
    /*  | 
    ||
356  | 
    * Set owner, group, mode for target; do the chown first,  | 
    ||
357  | 
    * chown may lose the setuid bits.  | 
    ||
358  | 
    */  | 
    ||
359  | 
    ✓✓✗✓ | 
    39814  | 
    if ((gid != (gid_t)-1 || uid != (uid_t)-1) &&  | 
    
360  | 
    19900  | 
    	    fchown(to_fd, uid, gid)) { | 
    |
361  | 
    serrno = errno;  | 
    ||
362  | 
    (void)unlink(safecopy ? tempfile : to_name);  | 
    ||
363  | 
    errx(1, "%s: chown/chgrp: %s",  | 
    ||
364  | 
    safecopy ? tempfile : to_name, strerror(serrno));  | 
    ||
365  | 
    }  | 
    ||
366  | 
    ✗✓ | 19914  | 
    	if (fchmod(to_fd, mode)) { | 
    
367  | 
    serrno = errno;  | 
    ||
368  | 
    (void)unlink(safecopy ? tempfile : to_name);  | 
    ||
369  | 
    errx(1, "%s: chmod: %s", safecopy ? tempfile : to_name,  | 
    ||
370  | 
    strerror(serrno));  | 
    ||
371  | 
    }  | 
    ||
372  | 
    |||
373  | 
    /*  | 
    ||
374  | 
    * If provided a set of flags, set them, otherwise, preserve the  | 
    ||
375  | 
    * flags, except for the dump flag.  | 
    ||
376  | 
    */  | 
    ||
377  | 
    ✗✓ | 19914  | 
    if (fchflags(to_fd,  | 
    
378  | 
    19914  | 
    	    flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) { | 
    |
379  | 
    if (errno != EOPNOTSUPP || (from_sb.st_flags & ~UF_NODUMP) != 0)  | 
    ||
380  | 
    			warnx("%s: chflags: %s", | 
    ||
381  | 
    safecopy ? tempfile :to_name, strerror(errno));  | 
    ||
382  | 
    }  | 
    ||
383  | 
    |||
384  | 
    ✗✓ | 19914  | 
    if (flags & USEFSYNC)  | 
    
385  | 
    fsync(to_fd);  | 
    ||
386  | 
    19914  | 
    (void)close(to_fd);  | 
    |
387  | 
    ✓✓ | 19914  | 
    if (!devnull)  | 
    
388  | 
    19911  | 
    (void)close(from_fd);  | 
    |
389  | 
    |||
390  | 
    /*  | 
    ||
391  | 
    * Move the new file into place if doing a safe copy  | 
    ||
392  | 
    * and the files are different (or just not compared).  | 
    ||
393  | 
    */  | 
    ||
394  | 
    ✓✓ | 19914  | 
    	if (safecopy && !files_match) { | 
    
395  | 
    /* Try to turn off the immutable bits. */  | 
    ||
396  | 
    ✗✓ | 1383  | 
    if (to_sb.st_flags & (NOCHANGEBITS))  | 
    
397  | 
    (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS));  | 
    ||
398  | 
    ✗✓ | 1383  | 
    		if (dobackup) { | 
    
399  | 
    char backup[PATH_MAX];  | 
    ||
400  | 
    (void)snprintf(backup, PATH_MAX, "%s%s", to_name,  | 
    ||
401  | 
    suffix);  | 
    ||
402  | 
    /* It is ok for the target file not to exist. */  | 
    ||
403  | 
    			if (rename(to_name, backup) < 0 && errno != ENOENT) { | 
    ||
404  | 
    serrno = errno;  | 
    ||
405  | 
    unlink(tempfile);  | 
    ||
406  | 
    errx(1, "rename: %s to %s: %s", to_name,  | 
    ||
407  | 
    backup, strerror(serrno));  | 
    ||
408  | 
    }  | 
    ||
409  | 
    }  | 
    ||
410  | 
    ✗✓ | 1383  | 
    		if (rename(tempfile, to_name) < 0 ) { | 
    
411  | 
    serrno = errno;  | 
    ||
412  | 
    unlink(tempfile);  | 
    ||
413  | 
    errx(1, "rename: %s to %s: %s", tempfile,  | 
    ||
414  | 
    to_name, strerror(serrno));  | 
    ||
415  | 
    }  | 
    ||
416  | 
    }  | 
    ||
417  | 
    19914  | 
    }  | 
    |
418  | 
    |||
419  | 
    /*  | 
    ||
420  | 
    * copy --  | 
    ||
421  | 
    * copy from one file to another  | 
    ||
422  | 
    */  | 
    ||
423  | 
    void  | 
    ||
424  | 
    copy(int from_fd, char *from_name, int to_fd, char *to_name, off_t size,  | 
    ||
425  | 
    int sparse)  | 
    ||
426  | 
    { | 
    ||
427  | 
    ssize_t nr, nw;  | 
    ||
428  | 
    int serrno;  | 
    ||
429  | 
    37526  | 
    char *p, buf[MAXBSIZE];  | 
    |
430  | 
    |||
431  | 
    ✗✓ | 18763  | 
    if (size == 0)  | 
    
432  | 
    return;  | 
    ||
433  | 
    |||
434  | 
    /* Rewind file descriptors. */  | 
    ||
435  | 
    ✗✓ | 18763  | 
    if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1)  | 
    
436  | 
    err(1, "lseek: %s", from_name);  | 
    ||
437  | 
    ✗✓ | 18763  | 
    if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1)  | 
    
438  | 
    err(1, "lseek: %s", to_name);  | 
    ||
439  | 
    |||
440  | 
    /*  | 
    ||
441  | 
    * Mmap and write if less than 8M (the limit is so we don't totally  | 
    ||
442  | 
    * trash memory on big files. This is really a minor hack, but it  | 
    ||
443  | 
    * wins some CPU back. Sparse files need special treatment.  | 
    ||
444  | 
    */  | 
    ||
445  | 
    ✓✓ | 18763  | 
    	if (!sparse && size <= 8 * 1048576) { | 
    
446  | 
    size_t siz;  | 
    ||
447  | 
    |||
448  | 
    ✗✓ | 37476  | 
    if ((p = mmap(NULL, (size_t)size, PROT_READ, MAP_PRIVATE,  | 
    
449  | 
    18738  | 
    		    from_fd, (off_t)0)) == MAP_FAILED) { | 
    |
450  | 
    serrno = errno;  | 
    ||
451  | 
    (void)unlink(to_name);  | 
    ||
452  | 
    errc(1, serrno, "%s", from_name);  | 
    ||
453  | 
    }  | 
    ||
454  | 
    18738  | 
    madvise(p, size, MADV_SEQUENTIAL);  | 
    |
455  | 
    siz = (size_t)size;  | 
    ||
456  | 
    ✗✓ | 18738  | 
    		if ((nw = write(to_fd, p, siz)) != siz) { | 
    
457  | 
    serrno = errno;  | 
    ||
458  | 
    (void)unlink(to_name);  | 
    ||
459  | 
    errx(1, "%s: %s",  | 
    ||
460  | 
    to_name, strerror(nw > 0 ? EIO : serrno));  | 
    ||
461  | 
    }  | 
    ||
462  | 
    18738  | 
    (void) munmap(p, (size_t)size);  | 
    |
463  | 
    18738  | 
    	} else { | 
    |
464  | 
    25  | 
    int sz, rem, isem = 1;  | 
    |
465  | 
    25  | 
    struct stat sb;  | 
    |
466  | 
    |||
467  | 
    /*  | 
    ||
468  | 
    * Pass the blocksize of the file being written to the write  | 
    ||
469  | 
    * routine. if the size is zero, use the default S_BLKSIZE.  | 
    ||
470  | 
    */  | 
    ||
471  | 
    ✓✗✗✓ | 
    50  | 
    if (fstat(to_fd, &sb) != 0 || sb.st_blksize == 0)  | 
    
472  | 
    sz = S_BLKSIZE;  | 
    ||
473  | 
    else  | 
    ||
474  | 
    sz = sb.st_blksize;  | 
    ||
475  | 
    25  | 
    rem = sz;  | 
    |
476  | 
    |||
477  | 
    ✓✓ | 5034  | 
    		while ((nr = read(from_fd, buf, sizeof(buf))) > 0) { | 
    
478  | 
    ✗✓ | 4984  | 
    if (sparse)  | 
    
479  | 
    nw = file_write(to_fd, buf, nr, &rem, &isem, sz);  | 
    ||
480  | 
    else  | 
    ||
481  | 
    4984  | 
    nw = write(to_fd, buf, nr);  | 
    |
482  | 
    ✓✗ | 4984  | 
    			if (nw != nr) { | 
    
483  | 
    serrno = errno;  | 
    ||
484  | 
    (void)unlink(to_name);  | 
    ||
485  | 
    errx(1, "%s: %s",  | 
    ||
486  | 
    to_name, strerror(nw > 0 ? EIO : serrno));  | 
    ||
487  | 
    }  | 
    ||
488  | 
    }  | 
    ||
489  | 
    ✗✓ | 25  | 
    if (sparse)  | 
    
490  | 
    file_flush(to_fd, isem);  | 
    ||
491  | 
    ✗✓ | 25  | 
    		if (nr != 0) { | 
    
492  | 
    serrno = errno;  | 
    ||
493  | 
    (void)unlink(to_name);  | 
    ||
494  | 
    errc(1, serrno, "%s", from_name);  | 
    ||
495  | 
    }  | 
    ||
496  | 
    25  | 
    }  | 
    |
497  | 
    37526  | 
    }  | 
    |
498  | 
    |||
499  | 
    /*  | 
    ||
500  | 
    * compare --  | 
    ||
501  | 
    * compare two files; non-zero means files differ  | 
    ||
502  | 
    */  | 
    ||
503  | 
    int  | 
    ||
504  | 
    compare(int from_fd, const char *from_name, off_t from_len, int to_fd,  | 
    ||
505  | 
    const char *to_name, off_t to_len)  | 
    ||
506  | 
    { | 
    ||
507  | 
    caddr_t p1, p2;  | 
    ||
508  | 
    size_t length;  | 
    ||
509  | 
    off_t from_off, to_off, remainder;  | 
    ||
510  | 
    int dfound;  | 
    ||
511  | 
    |||
512  | 
    ✗✓✗✗ | 
    2296  | 
    if (from_len == 0 && from_len == to_len)  | 
    
513  | 
    return (0);  | 
    ||
514  | 
    |||
515  | 
    ✗✓ | 1148  | 
    if (from_len != to_len)  | 
    
516  | 
    return (1);  | 
    ||
517  | 
    |||
518  | 
    /*  | 
    ||
519  | 
    * Compare the two files being careful not to mmap  | 
    ||
520  | 
    * more than 8M at a time.  | 
    ||
521  | 
    */  | 
    ||
522  | 
    from_off = to_off = (off_t)0;  | 
    ||
523  | 
    remainder = from_len;  | 
    ||
524  | 
    1148  | 
    	do { | 
    |
525  | 
    1148  | 
    length = MINIMUM(remainder, 8 * 1048576);  | 
    |
526  | 
    1148  | 
    remainder -= length;  | 
    |
527  | 
    |||
528  | 
    ✗✓ | 2296  | 
    if ((p1 = mmap(NULL, length, PROT_READ, MAP_PRIVATE,  | 
    
529  | 
    1148  | 
    from_fd, from_off)) == MAP_FAILED)  | 
    |
530  | 
    err(1, "%s", from_name);  | 
    ||
531  | 
    ✗✓ | 2296  | 
    if ((p2 = mmap(NULL, length, PROT_READ, MAP_PRIVATE,  | 
    
532  | 
    1148  | 
    to_fd, to_off)) == MAP_FAILED)  | 
    |
533  | 
    err(1, "%s", to_name);  | 
    ||
534  | 
    ✓✗ | 1148  | 
    		if (length) { | 
    
535  | 
    1148  | 
    madvise(p1, length, MADV_SEQUENTIAL);  | 
    |
536  | 
    1148  | 
    madvise(p2, length, MADV_SEQUENTIAL);  | 
    |
537  | 
    1148  | 
    }  | 
    |
538  | 
    |||
539  | 
    1148  | 
    dfound = memcmp(p1, p2, length);  | 
    |
540  | 
    |||
541  | 
    1148  | 
    (void) munmap(p1, length);  | 
    |
542  | 
    1148  | 
    (void) munmap(p2, length);  | 
    |
543  | 
    |||
544  | 
    1148  | 
    from_off += length;  | 
    |
545  | 
    1148  | 
    to_off += length;  | 
    |
546  | 
    |||
547  | 
    ✗✓ | 1148  | 
    } while (!dfound && remainder > 0);  | 
    
548  | 
    |||
549  | 
    1148  | 
    return(dfound);  | 
    |
550  | 
    1148  | 
    }  | 
    |
551  | 
    |||
552  | 
    /*  | 
    ||
553  | 
    * strip --  | 
    ||
554  | 
    * use strip(1) to strip the target file  | 
    ||
555  | 
    */  | 
    ||
556  | 
    void  | 
    ||
557  | 
    strip(char *to_name)  | 
    ||
558  | 
    { | 
    ||
559  | 
    2162  | 
    int serrno, status;  | 
    |
560  | 
    1081  | 
    char * volatile path_strip;  | 
    |
561  | 
    pid_t pid;  | 
    ||
562  | 
    |||
563  | 
    ✓✗✓✗ | 
    2162  | 
    	if (issetugid() || (path_strip = getenv("STRIP")) == NULL) | 
    
564  | 
    1081  | 
    path_strip = _PATH_STRIP;  | 
    |
565  | 
    |||
566  | 
    ✗✗✓ | 1081  | 
    	switch ((pid = vfork())) { | 
    
567  | 
    case -1:  | 
    ||
568  | 
    serrno = errno;  | 
    ||
569  | 
    (void)unlink(to_name);  | 
    ||
570  | 
    errc(1, serrno, "forks");  | 
    ||
571  | 
    case 0:  | 
    ||
572  | 
    execl(path_strip, "strip", "--", to_name, (char *)NULL);  | 
    ||
573  | 
    		warn("%s", path_strip); | 
    ||
574  | 
    _exit(1);  | 
    ||
575  | 
    default:  | 
    ||
576  | 
    ✗✓ | 1081  | 
    		while (waitpid(pid, &status, 0) == -1) { | 
    
577  | 
    if (errno != EINTR)  | 
    ||
578  | 
    break;  | 
    ||
579  | 
    }  | 
    ||
580  | 
    ✗✓ | 1081  | 
    if (!WIFEXITED(status))  | 
    
581  | 
    (void)unlink(to_name);  | 
    ||
582  | 
    }  | 
    ||
583  | 
    1081  | 
    }  | 
    |
584  | 
    |||
585  | 
    /*  | 
    ||
586  | 
    * install_dir --  | 
    ||
587  | 
    * build directory hierarchy  | 
    ||
588  | 
    */  | 
    ||
589  | 
    void  | 
    ||
590  | 
    install_dir(char *path, int mode)  | 
    ||
591  | 
    { | 
    ||
592  | 
    char *p;  | 
    ||
593  | 
    16556  | 
    struct stat sb;  | 
    |
594  | 
    int ch;  | 
    ||
595  | 
    |||
596  | 
    182438  | 
    for (p = path;; ++p)  | 
    |
597  | 
    ✓✓✓✓ ✓✓  | 
    522480  | 
    		if (!*p || (p != path && *p  == '/')) { | 
    
598  | 
    33109  | 
    ch = *p;  | 
    |
599  | 
    33109  | 
    *p = '\0';  | 
    |
600  | 
    ✓✓ | 33109  | 
    			if (mkdir(path, 0777)) { | 
    
601  | 
    33073  | 
    int mkdir_errno = errno;  | 
    |
602  | 
    ✗✓ | 33073  | 
    				if (stat(path, &sb)) { | 
    
603  | 
    /* Not there; use mkdir()s errno */  | 
    ||
604  | 
    errc(1, mkdir_errno, "%s",  | 
    ||
605  | 
    path);  | 
    ||
606  | 
    /* NOTREACHED */  | 
    ||
607  | 
    }  | 
    ||
608  | 
    ✗✓ | 33073  | 
    				if (!S_ISDIR(sb.st_mode)) { | 
    
609  | 
    /* Is there, but isn't a directory */  | 
    ||
610  | 
    errc(1, ENOTDIR, "%s", path);  | 
    ||
611  | 
    /* NOTREACHED */  | 
    ||
612  | 
    }  | 
    ||
613  | 
    33073  | 
    }  | 
    |
614  | 
    ✓✓ | 33109  | 
    if (!(*p = ch))  | 
    
615  | 
    break;  | 
    ||
616  | 
    }  | 
    ||
617  | 
    |||
618  | 
    ✓✓✓✗ ✗✓  | 
    24818  | 
    if (((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid)) ||  | 
    
619  | 
    8278  | 
    	    chmod(path, mode)) { | 
    |
620  | 
    		warn("%s", path); | 
    ||
621  | 
    }  | 
    ||
622  | 
    8278  | 
    }  | 
    |
623  | 
    |||
624  | 
    /*  | 
    ||
625  | 
    * usage --  | 
    ||
626  | 
    * print a usage message and die  | 
    ||
627  | 
    */  | 
    ||
628  | 
    void  | 
    ||
629  | 
    usage(void)  | 
    ||
630  | 
    { | 
    ||
631  | 
    (void)fprintf(stderr, "\  | 
    ||
632  | 
    usage: install [-bCcDdFpSs] [-B suffix] [-f flags] [-g group] [-m mode] [-o owner]\n source ... target ...\n");  | 
    ||
633  | 
    exit(1);  | 
    ||
634  | 
    /* NOTREACHED */  | 
    ||
635  | 
    }  | 
    ||
636  | 
    |||
637  | 
    /*  | 
    ||
638  | 
    * create_tempfile --  | 
    ||
639  | 
    * create a temporary file based on path and open it  | 
    ||
640  | 
    */  | 
    ||
641  | 
    int  | 
    ||
642  | 
    create_tempfile(char *path, char *temp, size_t tsize)  | 
    ||
643  | 
    { | 
    ||
644  | 
    char *p;  | 
    ||
645  | 
    |||
646  | 
    2766  | 
    strlcpy(temp, path, tsize);  | 
    |
647  | 
    ✓✗ | 1383  | 
    if ((p = strrchr(temp, '/')) != NULL)  | 
    
648  | 
    1383  | 
    p++;  | 
    |
649  | 
    else  | 
    ||
650  | 
    p = temp;  | 
    ||
651  | 
    1383  | 
    *p = '\0';  | 
    |
652  | 
    1383  | 
    strlcat(p, "INS@XXXXXXXXXX", tsize);  | 
    |
653  | 
    |||
654  | 
    1383  | 
    return(mkstemp(temp));  | 
    |
655  | 
    }  | 
    ||
656  | 
    |||
657  | 
    /*  | 
    ||
658  | 
    * create_newfile --  | 
    ||
659  | 
    * create a new file, overwriting an existing one if necessary  | 
    ||
660  | 
    */  | 
    ||
661  | 
    int  | 
    ||
662  | 
    create_newfile(char *path, struct stat *sbp)  | 
    ||
663  | 
    { | 
    ||
664  | 
    34766  | 
    char backup[PATH_MAX];  | 
    |
665  | 
    |||
666  | 
    /*  | 
    ||
667  | 
    * Unlink now... avoid ETXTBSY errors later. Try and turn  | 
    ||
668  | 
    * off the append/immutable bits -- if we fail, go ahead,  | 
    ||
669  | 
    * it might work.  | 
    ||
670  | 
    */  | 
    ||
671  | 
    ✗✓ | 17383  | 
    if (sbp->st_flags & (NOCHANGEBITS))  | 
    
672  | 
    (void)chflags(path, sbp->st_flags & ~(NOCHANGEBITS));  | 
    ||
673  | 
    |||
674  | 
    ✓✓ | 17383  | 
    	if (dobackup) { | 
    
675  | 
    2  | 
    (void)snprintf(backup, PATH_MAX, "%s%s", path, suffix);  | 
    |
676  | 
    /* It is ok for the target file not to exist. */  | 
    ||
677  | 
    ✓✗✗✓ | 
    4  | 
    if (rename(path, backup) < 0 && errno != ENOENT)  | 
    
678  | 
    err(1, "rename: %s to %s (errno %d)", path, backup, errno);  | 
    ||
679  | 
    	} else { | 
    ||
680  | 
    ✓✓✗✓ | 
    17395  | 
    if (unlink(path) < 0 && errno != ENOENT)  | 
    
681  | 
    err(1, "%s", path);  | 
    ||
682  | 
    }  | 
    ||
683  | 
    |||
684  | 
    34766  | 
    return(open(path, O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR));  | 
    |
685  | 
    17383  | 
    }  | 
    |
686  | 
    |||
687  | 
    /*  | 
    ||
688  | 
    * file_write()  | 
    ||
689  | 
    * Write/copy a file (during copy or archive extract). This routine knows  | 
    ||
690  | 
    * how to copy files with lseek holes in it. (Which are read as file  | 
    ||
691  | 
    * blocks containing all 0's but do not have any file blocks associated  | 
    ||
692  | 
    * with the data). Typical examples of these are files created by dbm  | 
    ||
693  | 
    * variants (.pag files). While the file size of these files are huge, the  | 
    ||
694  | 
    * actual storage is quite small (the files are sparse). The problem is  | 
    ||
695  | 
    * the holes read as all zeros so are probably stored on the archive that  | 
    ||
696  | 
    * way (there is no way to determine if the file block is really a hole,  | 
    ||
697  | 
    * we only know that a file block of all zero's can be a hole).  | 
    ||
698  | 
    * At this writing, no major archive format knows how to archive files  | 
    ||
699  | 
    * with holes. However, on extraction (or during copy, -rw) we have to  | 
    ||
700  | 
    * deal with these files. Without detecting the holes, the files can  | 
    ||
701  | 
    * consume a lot of file space if just written to disk. This replacement  | 
    ||
702  | 
    * for write when passed the basic allocation size of a file system block,  | 
    ||
703  | 
    * uses lseek whenever it detects the input data is all 0 within that  | 
    ||
704  | 
    * file block. In more detail, the strategy is as follows:  | 
    ||
705  | 
    * While the input is all zero keep doing an lseek. Keep track of when we  | 
    ||
706  | 
    * pass over file block boundaries. Only write when we hit a non zero  | 
    ||
707  | 
    * input. once we have written a file block, we continue to write it to  | 
    ||
708  | 
    * the end (we stop looking at the input). When we reach the start of the  | 
    ||
709  | 
    * next file block, start checking for zero blocks again. Working on file  | 
    ||
710  | 
    * block boundaries significantly reduces the overhead when copying files  | 
    ||
711  | 
    * that are NOT very sparse. This overhead (when compared to a write) is  | 
    ||
712  | 
    * almost below the measurement resolution on many systems. Without it,  | 
    ||
713  | 
    * files with holes cannot be safely copied. It does has a side effect as  | 
    ||
714  | 
    * it can put holes into files that did not have them before, but that is  | 
    ||
715  | 
    * not a problem since the file contents are unchanged (in fact it saves  | 
    ||
716  | 
    * file space). (Except on paging files for diskless clients. But since we  | 
    ||
717  | 
    * cannot determine one of those file from here, we ignore them). If this  | 
    ||
718  | 
    * ever ends up on a system where CTG files are supported and the holes  | 
    ||
719  | 
    * are not desired, just do a conditional test in those routines that  | 
    ||
720  | 
    * call file_write() and have it call write() instead. BEFORE CLOSING THE  | 
    ||
721  | 
    * FILE, make sure to call file_flush() when the last write finishes with  | 
    ||
722  | 
    * an empty block. A lot of file systems will not create an lseek hole at  | 
    ||
723  | 
    * the end. In this case we drop a single 0 at the end to force the  | 
    ||
724  | 
    * trailing 0's in the file.  | 
    ||
725  | 
    * ---Parameters---  | 
    ||
726  | 
    * rem: how many bytes left in this file system block  | 
    ||
727  | 
    * isempt: have we written to the file block yet (is it empty)  | 
    ||
728  | 
    * sz: basic file block allocation size  | 
    ||
729  | 
    * cnt: number of bytes on this write  | 
    ||
730  | 
    * str: buffer to write  | 
    ||
731  | 
    * Return:  | 
    ||
732  | 
    * number of bytes written, -1 on write (or lseek) error.  | 
    ||
733  | 
    */  | 
    ||
734  | 
    |||
735  | 
    int  | 
    ||
736  | 
    file_write(int fd, char *str, size_t cnt, int *rem, int *isempt, int sz)  | 
    ||
737  | 
    { | 
    ||
738  | 
    char *pt;  | 
    ||
739  | 
    char *end;  | 
    ||
740  | 
    size_t wcnt;  | 
    ||
741  | 
    char *st = str;  | 
    ||
742  | 
    |||
743  | 
    /*  | 
    ||
744  | 
    * while we have data to process  | 
    ||
745  | 
    */  | 
    ||
746  | 
    	while (cnt) { | 
    ||
747  | 
    		if (!*rem) { | 
    ||
748  | 
    /*  | 
    ||
749  | 
    * We are now at the start of file system block again  | 
    ||
750  | 
    * (or what we think one is...). start looking for  | 
    ||
751  | 
    * empty blocks again  | 
    ||
752  | 
    */  | 
    ||
753  | 
    *isempt = 1;  | 
    ||
754  | 
    *rem = sz;  | 
    ||
755  | 
    }  | 
    ||
756  | 
    |||
757  | 
    /*  | 
    ||
758  | 
    * only examine up to the end of the current file block or  | 
    ||
759  | 
    * remaining characters to write, whatever is smaller  | 
    ||
760  | 
    */  | 
    ||
761  | 
    wcnt = MINIMUM(cnt, *rem);  | 
    ||
762  | 
    cnt -= wcnt;  | 
    ||
763  | 
    *rem -= wcnt;  | 
    ||
764  | 
    		if (*isempt) { | 
    ||
765  | 
    /*  | 
    ||
766  | 
    * have not written to this block yet, so we keep  | 
    ||
767  | 
    * looking for zero's  | 
    ||
768  | 
    */  | 
    ||
769  | 
    pt = st;  | 
    ||
770  | 
    end = st + wcnt;  | 
    ||
771  | 
    |||
772  | 
    /*  | 
    ||
773  | 
    * look for a zero filled buffer  | 
    ||
774  | 
    */  | 
    ||
775  | 
    while ((pt < end) && (*pt == '\0'))  | 
    ||
776  | 
    ++pt;  | 
    ||
777  | 
    |||
778  | 
    			if (pt == end) { | 
    ||
779  | 
    /*  | 
    ||
780  | 
    * skip, buf is empty so far  | 
    ||
781  | 
    */  | 
    ||
782  | 
    				if (lseek(fd, (off_t)wcnt, SEEK_CUR) < 0) { | 
    ||
783  | 
    					warn("lseek"); | 
    ||
784  | 
    return(-1);  | 
    ||
785  | 
    }  | 
    ||
786  | 
    st = pt;  | 
    ||
787  | 
    continue;  | 
    ||
788  | 
    }  | 
    ||
789  | 
    /*  | 
    ||
790  | 
    * drat, the buf is not zero filled  | 
    ||
791  | 
    */  | 
    ||
792  | 
    *isempt = 0;  | 
    ||
793  | 
    }  | 
    ||
794  | 
    |||
795  | 
    /*  | 
    ||
796  | 
    * have non-zero data in this file system block, have to write  | 
    ||
797  | 
    */  | 
    ||
798  | 
    		if (write(fd, st, wcnt) != wcnt) { | 
    ||
799  | 
    			warn("write"); | 
    ||
800  | 
    return(-1);  | 
    ||
801  | 
    }  | 
    ||
802  | 
    st += wcnt;  | 
    ||
803  | 
    }  | 
    ||
804  | 
    return(st - str);  | 
    ||
805  | 
    }  | 
    ||
806  | 
    |||
807  | 
    /*  | 
    ||
808  | 
    * file_flush()  | 
    ||
809  | 
    * when the last file block in a file is zero, many file systems will not  | 
    ||
810  | 
    * let us create a hole at the end. To get the last block with zeros, we  | 
    ||
811  | 
    * write the last BYTE with a zero (back up one byte and write a zero).  | 
    ||
812  | 
    */  | 
    ||
813  | 
    void  | 
    ||
814  | 
    file_flush(int fd, int isempt)  | 
    ||
815  | 
    { | 
    ||
816  | 
    static char blnk[] = "\0";  | 
    ||
817  | 
    |||
818  | 
    /*  | 
    ||
819  | 
    * silly test, but make sure we are only called when the last block is  | 
    ||
820  | 
    * filled with all zeros.  | 
    ||
821  | 
    */  | 
    ||
822  | 
    if (!isempt)  | 
    ||
823  | 
    return;  | 
    ||
824  | 
    |||
825  | 
    /*  | 
    ||
826  | 
    * move back one byte and write a zero  | 
    ||
827  | 
    */  | 
    ||
828  | 
    	if (lseek(fd, (off_t)-1, SEEK_CUR) < 0) { | 
    ||
829  | 
    		warn("Failed seek on file"); | 
    ||
830  | 
    return;  | 
    ||
831  | 
    }  | 
    ||
832  | 
    |||
833  | 
    if (write(fd, blnk, 1) < 0)  | 
    ||
834  | 
    		warn("Failed write to file"); | 
    ||
835  | 
    return;  | 
    ||
836  | 
    }  | 
    
| Generated by: GCOVR (Version 3.3) |