xref: /illumos-gate/usr/src/ucbcmd/touch/touch.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdio.h>
28 #include <ctype.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/file.h>
32 #include <sys/time.h>
33 #include <time.h>
34 #include <errno.h>
35 #include <unistd.h>
36 
37 #define	isleap(y) (((y) % 4) == 0 && ((y) % 100) != 0 || ((y) % 400) == 0)
38 
39 struct	stat	stbuf;
40 int	status;
41 #ifdef S5EMUL
42 int dmsize[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
43 #endif /* S5EMUL */
44 
45 static char usage[] =
46 #ifdef S5EMUL
47 		"[-amc] [mmddhhmm[yy]]";
48 #else /*!S5EMUL*/
49 		"[-amcf]";
50 int	force = 0;
51 int	nowrite;
52 #endif /*!S5EMUL*/
53 
54 int	mflg=1, aflg=1, cflg=0, nflg=0;
55 char	*prog;
56 
57 #ifdef S5EMUL
58 char	*cbp;
59 #endif /* S5EMUL */
60 time_t	time();
61 off_t	lseek();
62 time_t	timelocal(), timegm();
63 struct timeval timbuf;
64 static void timestruc_to_timeval(timestruc_t *, struct timeval *);
65 
66 #ifdef S5EMUL
67 struct tm *
68 gtime()
69 {
70 	static struct tm newtime;
71 	long nt;
72 
73 	newtime.tm_mon = gpair() - 1;
74 	newtime.tm_mday = gpair();
75 	newtime.tm_hour = gpair();
76 	if (newtime.tm_hour == 24) {
77 		newtime.tm_hour = 0;
78 		newtime.tm_mday++;
79 	}
80 	newtime.tm_min = gpair();
81 	newtime.tm_sec = 0;
82 	newtime.tm_year = gpair();
83 	if (newtime.tm_year < 0) {
84 		(void) time(&nt);
85 		newtime.tm_year = localtime(&nt)->tm_year;
86 	}
87 	return (&newtime);
88 }
89 
90 gpair()
91 {
92 	int c, d;
93 	char *cp;
94 
95 	cp = cbp;
96 	if (*cp == 0)
97 		return (-1);
98 	c = (*cp++ - '0') * 10;
99 	if (c<0 || c>100)
100 		return (-1);
101 	if (*cp == 0)
102 		return (-1);
103 	if ((d = *cp++ - '0') < 0 || d > 9)
104 		return (-1);
105 	cbp = cp;
106 	return (c+d);
107 }
108 #endif /*S5EMUL*/
109 
110 int
111 main(int argc, char *argv[])
112 {
113 	int c;
114 #ifdef S5EMUL
115 	int days_in_month;
116 	struct tm *tp;
117 #endif /* S5EMUL */
118 
119 	int errflg=0, optc;
120 	extern char *optarg;
121 	extern int optind;
122 	extern int opterr;
123 
124 	prog = argv[0];
125 	opterr = 0;			/* disable getopt() error msgs */
126 	while ((optc=getopt(argc, argv, "amcf")) != EOF)
127 		switch (optc) {
128 		case 'm':
129 			mflg++;
130 			aflg--;
131 			break;
132 		case 'a':
133 			aflg++;
134 			mflg--;
135 			break;
136 		case 'c':
137 			cflg++;
138 			break;
139 #ifndef S5EMUL
140 		case 'f':
141 			force++;	/* SysV version ignores -f */
142 			break;
143 #endif /*!S5EMUL*/
144 		case '?':
145 			errflg++;
146 		}
147 
148 	if (((argc-optind) < 1) || errflg) {
149 		(void) fprintf(stderr, "usage: %s %s file ...\n", prog, usage);
150 		exit(2);
151 	}
152 	status = 0;
153 
154 #ifdef S5EMUL
155 	if (!isnumber(argv[optind])) {	/* BSD version only sets Present */
156 #endif /*S5EMUL*/
157 		if ((aflg <= 0) || (mflg <= 0))
158 			(void) gettimeofday(&timbuf, NULL);
159 		else
160 			nflg++;		/* no -a, -m, or date seen */
161 #ifdef S5EMUL
162 	} else {			/* SysV version sets arbitrary date */
163 		cbp = (char *)argv[optind++];
164 		if ((tp = gtime()) == NULL) {
165 			(void) fprintf(stderr, "%s: bad date conversion\n",
166 			    prog);
167 			exit(2);
168 		}
169 		days_in_month = dmsize[tp->tm_mon];
170 		if (tp->tm_mon == 1 && isleap(tp->tm_year + 1900))
171 			days_in_month = 29;	/* February in leap year */
172 		if (tp->tm_mon < 0 || tp->tm_mon > 11 ||
173 		    tp->tm_mday < 1 || tp->tm_mday > days_in_month ||
174 		    tp->tm_hour < 0 || tp->tm_hour > 23 ||
175 		    tp->tm_min < 0 || tp->tm_min > 59 ||
176 		    tp->tm_sec < 0 || tp->tm_sec > 59) {
177 			(void) fprintf(stderr, "%s: bad date conversion\n",
178 			    prog);
179 			exit(2);
180 		}
181 		timbuf = timelocal(tp);
182 	}
183 #endif /*S5EMUL*/
184 
185 	for (c = optind; c < argc; c++) {
186 		if (touch(argv[c]) < 0)
187 			status++;
188 	}
189 	return (status);
190 }
191 
192 int
193 touch(filename)
194 	char *filename;
195 {
196 	struct timeval times[2];
197 	int fd;
198 
199 	if (stat(filename, &stbuf)) {
200 		/*
201 		 * if stat failed for reasons other than ENOENT,
202 		 * the file should not be created, since this
203 		 * can clobber the contents of an existing file
204 		 * (for example, a large file that results in overflow).
205 		 */
206 		if (errno != ENOENT) {
207 			(void) fprintf(stderr,"%s: cannot stat ", prog);
208 			perror(filename);
209 			return (-1);
210 		} else if (cflg) {
211 			return (-1);
212 		}
213 		else if ((fd = creat(filename, 0666)) < 0) {
214 			(void) fprintf(stderr, "%s: cannot create ", prog);
215 			perror(filename);
216 			return (-1);
217 		}
218 		else {
219 			(void) close(fd);
220 			if (stat(filename, &stbuf)) {
221 				(void) fprintf(stderr,"%s: cannot stat ", prog);
222 				perror(filename);
223 				return (-1);
224 			}
225 		}
226 		if (nflg)
227 			return (0);
228 	}
229 
230 	times[0] = times[1] = timbuf;
231 	if (mflg <= 0)
232 		timestruc_to_timeval(&stbuf.st_mtim, times + 1);
233 	if (aflg <= 0)
234 		timestruc_to_timeval(&stbuf.st_atim, times);
235 
236 #ifndef S5EMUL
237 	/*
238 	 * Since utime() allows the owner to change file times without
239 	 * regard to access permission, enforce BSD semantics here
240 	 * (cannot touch if read-only and not -f).
241 	 */
242 	nowrite = access(filename, R_OK|W_OK);
243 	if (nowrite && !force) {
244 		(void) fprintf(stderr,
245 		    "%s: cannot touch %s: no write permission\n",
246 		    prog, filename);
247 		return (-1);
248 	}
249 #endif /*!S5EMUL*/
250 
251 	if (utimes(filename, nflg ? NULL : times)) {
252 		if (nflg && (errno != EROFS) && (errno != EACCES)) {
253 			/*
254 			 * If utime() failed to set the Present, it
255 			 * could be a BSD server that is complaining.
256 			 * If that's the case, try the old read/write trick.
257 			 */
258 			return (oldtouch(filename, &stbuf));
259 		}
260 		(void) fprintf(stderr,"%s: cannot change times on ", prog);
261 		perror(filename);
262 		return (-1);
263 	}
264 	return (0);
265 }
266 
267 int
268 oldtouch(filename, statp)
269 	char *filename;
270 	struct stat *statp;
271 {
272 	int rwstatus;
273 
274 	if ((statp->st_mode & S_IFMT) != S_IFREG) {
275 		(void) fprintf(stderr,
276 		    "%s: %s: only owner may touch special files on this filesystem\n",
277 		    prog, filename);
278 		return (-1);
279 	}
280 
281 #ifndef S5EMUL
282 	if (nowrite && force) {
283 		if (chmod(filename, 0666)) {
284 			fprintf(stderr, "%s: could not chmod ", prog);
285 			perror(filename);
286 			return (-1);
287 		}
288 		rwstatus = readwrite(filename, statp->st_size);
289 		if (chmod(filename, (int)statp->st_mode)) {
290 			fprintf(stderr, "%s: could not chmod back ", prog);
291 			perror(filename);
292 			return (-1);
293 		}
294 		return (rwstatus);
295 	} else
296 #endif /*!S5EMUL*/
297 		return (readwrite(filename, statp->st_size));
298 }
299 
300 int
301 readwrite(filename, size)
302 	char	*filename;
303 	off_t	size;
304 {
305 	int fd;
306 	char first;
307 
308 	if (size) {
309 		if ((fd = open(filename, 2)) < 0)
310 			goto error;
311 		if (read(fd, &first, 1) != 1)
312 			goto closeerror;
313 		if (lseek(fd, 0L, 0) == -1)
314 			goto closeerror;
315 		if (write(fd, &first, 1) != 1)
316 			goto closeerror;
317 	} else {
318 		if ((fd = creat(filename, 0666)) < 0)
319 			goto error;
320 	}
321 	if (close(fd) < 0)
322 		goto error;
323 	return (0);
324 
325 closeerror:
326 	(void) close(fd);
327 error:
328 	(void) fprintf(stderr, "%s: could not touch ", prog);
329 	perror(filename);
330 	return (-1);
331 }
332 
333 #ifdef S5EMUL
334 isnumber(s)
335 	char *s;
336 {
337 	char c;
338 
339 	while (c = *s++)
340 		if (!isdigit(c))
341 			return (0);
342 	return (1);
343 }
344 #endif /* S5EMUL */
345 
346 /*
347  * nanoseconds are rounded off to microseconds by flooring.
348  */
349 static void
350 timestruc_to_timeval(timestruc_t *ts, struct timeval *tv)
351 {
352 	tv->tv_sec = ts->tv_sec;
353 	tv->tv_usec = ts->tv_nsec / 1000;
354 }
355