comparison toys/posix/cpio.c @ 1271:c214de62b18b draft

Teach cpio to set uid/gid and timestamp. (Timestamp has year 2100 problem.) Note that directory timestamps are still sometimes wrong because creating things in a directory can update the timestamp. Also, cp -r has logic to ensure we can write to a directory that doesn't have write permission, cpio does not. This is fixable, but not what existing cpio does.
author Rob Landley <rob@landley.net>
date Tue, 29 Apr 2014 06:03:17 -0500
parents ee5a6875d695
children 11fe363078b8
comparison
equal deleted inserted replaced
1270:e81b951bf725 1271:c214de62b18b
37 char *archive; 37 char *archive;
38 char *fmt; 38 char *fmt;
39 ) 39 )
40 40
41 // Read strings, tail padded to 4 byte alignment. Argument "align" is amount 41 // Read strings, tail padded to 4 byte alignment. Argument "align" is amount
42 // by which start of string isn't aligned (usually 0). 42 // by which start of string isn't aligned (usually 0, but header is 110 bytes
43 // which is 2 bytes off because the first field wasn't expanded from 6 to 8).
43 static char *strpad(int fd, unsigned len, unsigned align) 44 static char *strpad(int fd, unsigned len, unsigned align)
44 { 45 {
45 char *str; 46 char *str;
46 47
47 align = (align + len) & 3; 48 align = (align + len) & 3;
48 if (align) len += (4-align); 49 if (align) len += (4-align);
49
50 xreadall(fd, str = xmalloc(len+1), len); 50 xreadall(fd, str = xmalloc(len+1), len);
51 str[len]=0; // redundant, in case archive is bad 51 str[len]=0; // redundant, in case archive is bad
52 52
53 return str; 53 return str;
54 } 54 }
86 86
87 // read cpio archive 87 // read cpio archive
88 88
89 if (toys.optflags & (FLAG_i|FLAG_t)) for (;;) { 89 if (toys.optflags & (FLAG_i|FLAG_t)) for (;;) {
90 char *name, *tofree, *data; 90 char *name, *tofree, *data;
91 unsigned size, mode; 91 unsigned size, mode, uid, gid, timestamp;
92 int test = toys.optflags & FLAG_t, err = 0; 92 int test = toys.optflags & FLAG_t, err = 0;
93 93
94 // Read header and name. 94 // Read header and name.
95 xreadall(afd, toybuf, 110); 95 xreadall(afd, toybuf, 110);
96 tofree = name = strpad(afd, x8u(toybuf+94), 110); 96 tofree = name = strpad(afd, x8u(toybuf+94), 110);
97 if (!strcmp("TRAILER!!!", name)) break; 97 if (!strcmp("TRAILER!!!", name)) break;
98 98
99 // If you want to extract absolute paths, "cd /" and run cpio. 99 // If you want to extract absolute paths, "cd /" and run cpio.
100 while (*name == '/') name++; 100 while (*name == '/') name++;
101 101 // TODO: remove .. entries
102 // Align to 4 bytes. Note header is 110 bytes which is 2 bytes over.
103 102
104 size = x8u(toybuf+54); 103 size = x8u(toybuf+54);
105 mode = x8u(toybuf+14); 104 mode = x8u(toybuf+14);
105 uid = x8u(toybuf+30);
106 gid = x8u(toybuf+38);
107 timestamp = x8u(toybuf+46); // unsigned 32 bit, so year 2100 problem
106 108
107 if (toys.optflags & (FLAG_t|FLAG_v)) puts(name); 109 if (toys.optflags & (FLAG_t|FLAG_v)) puts(name);
108 110
109 if (!test && strrchr(name, '/') && mkpathat(AT_FDCWD, name, 0, 2)) { 111 if (!test && strrchr(name, '/') && mkpathat(AT_FDCWD, name, 0, 2)) {
110 perror_msg("mkpath '%s'", name); 112 perror_msg("mkpath '%s'", name);
117 if (S_ISDIR(mode)) { 119 if (S_ISDIR(mode)) {
118 if (!test) err = mkdir(name, mode); 120 if (!test) err = mkdir(name, mode);
119 } else if (S_ISLNK(mode)) { 121 } else if (S_ISLNK(mode)) {
120 data = strpad(afd, size, 0); 122 data = strpad(afd, size, 0);
121 if (!test) err = symlink(data, name); 123 if (!test) err = symlink(data, name);
124 // Can't get a filehandle to a symlink, so do special chown
125 if (!err && !getpid()) err = lchown(name, uid, gid);
122 } else if (S_ISREG(mode)) { 126 } else if (S_ISREG(mode)) {
123 int fd; 127 int fd = test ? 0 : open(name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW, mode);
124 128
125 // If write fails, we still need to read/discard data to continue with 129 // If write fails, we still need to read/discard data to continue with
126 // archive. Since doing so overwrites errno, report error now 130 // archive. Since doing so overwrites errno, report error now
127 fd = test ? 0 : open(name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW, mode);
128 if (fd < 0) { 131 if (fd < 0) {
129 perror_msg("create %s", name); 132 perror_msg("create %s", name);
130 test++; 133 test++;
131 } 134 }
132 135
139 free(data); 142 free(data);
140 break; 143 break;
141 } 144 }
142 size -= sizeof(toybuf); 145 size -= sizeof(toybuf);
143 } 146 }
144 if (!test) close(fd); 147
148 if (!test) {
149 // set owner, restore dropped suid bit
150 if (!getpid()) {
151 err = fchown(fd, uid, gid);
152 if (!err) err = fchmod(fd, mode);
153 }
154 close(fd);
155 }
145 } else if (!test) 156 } else if (!test)
146 err = mknod(name, mode, makedev(x8u(toybuf+62), x8u(toybuf+70))); 157 err = mknod(name, mode, makedev(x8u(toybuf+62), x8u(toybuf+70)));
147 158
148 if (err<0) perror_msg("create '%s'", name); 159 // Set ownership and timestamp.
160 if (!test && !err) {
161 // Creading dir/dev doesn't give us a filehandle, we have to refer to it
162 // by name to chown/utime, but how do we know it's the same item?
163 // Check that we at least have the right type of entity open, and do
164 // NOT restore dropped suid bit in this case.
165 if (!S_ISREG(mode) && !S_ISLNK(mode) && !getpid()) {
166 int fd = open(name, O_WRONLY|O_NOFOLLOW);
167 struct stat st;
168
169 if (fd != -1 && !fstat(fd, &st) && (st.st_mode&S_IFMT) == mode)
170 err = fchown(fd, uid, gid);
171 else err = 1;
172
173 close(fd);
174 }
175
176 // set timestamp
177 if (!err) {
178 struct timespec times[2];
179
180 memset(times, 0, sizeof(struct timespec)*2);
181 times[0].tv_sec = times[1].tv_sec = timestamp;
182 err = utimensat(AT_FDCWD, name, times, AT_SYMLINK_NOFOLLOW);
183 }
184 }
185
186 if (err) perror_msg("'%s'", name);
149 free(tofree); 187 free(tofree);
150 188
151 // Output cpio archive 189 // Output cpio archive
152 190
153 } else { 191 } else {