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