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