file.c (11274B)
1 /* file.c 2 * (c) 2002 Mikulas Patocka 3 * This file is a part of the Links program, released under GPL. 4 */ 5 6 #include <limits.h> 7 #include <sys/stat.h> 8 9 #include "links.h" 10 11 static void 12 setrwx(unsigned m, unsigned char *p) 13 { 14 if (m & S_IRUSR) 15 p[0] = 'r'; 16 if (m & S_IWUSR) 17 p[1] = 'w'; 18 if (m & S_IXUSR) 19 p[2] = 'x'; 20 } 21 22 static void 23 setst(unsigned m, unsigned char *p) 24 { 25 #ifdef S_ISUID 26 if (m & S_ISUID) { 27 p[2] = 'S'; 28 if (m & S_IXUSR) 29 p[2] = 's'; 30 } 31 #endif 32 #ifdef S_ISGID 33 if (m & S_ISGID) { 34 p[5] = 'S'; 35 if (m & S_IXGRP) 36 p[5] = 's'; 37 } 38 #endif 39 #ifdef S_ISVTX 40 if (m & S_ISVTX) { 41 p[8] = 'T'; 42 if (m & S_IXOTH) 43 p[8] = 't'; 44 } 45 #endif 46 } 47 48 static size_t 49 stat_mode(unsigned char **p, size_t l, struct stat *stp) 50 { 51 unsigned char c = '?'; 52 unsigned char rwx[10] = "---------"; 53 if (stp) { 54 if (0) { 55 } 56 #ifdef S_ISBLK 57 else if (S_ISBLK(stp->st_mode)) 58 c = 'b'; 59 #endif 60 #ifdef S_ISCHR 61 else if (S_ISCHR(stp->st_mode)) 62 c = 'c'; 63 #endif 64 else if (S_ISDIR(stp->st_mode)) 65 c = 'd'; 66 else if (S_ISREG(stp->st_mode)) 67 c = '-'; 68 #ifdef S_ISFIFO 69 else if (S_ISFIFO(stp->st_mode)) 70 c = 'p'; 71 #endif 72 #ifdef S_ISLNK 73 else if (S_ISLNK(stp->st_mode)) 74 c = 'l'; 75 #endif 76 #ifdef S_ISSOCK 77 else if (S_ISSOCK(stp->st_mode)) 78 c = 's'; 79 #endif 80 #ifdef S_ISNWK 81 else if (S_ISNWK(stp->st_mode)) 82 c = 'n'; 83 #endif 84 } 85 l = add_chr_to_str(p, l, c); 86 if (stp) { 87 unsigned mode = stp->st_mode; 88 setrwx(mode << 0, &rwx[0]); 89 setrwx(mode << 3, &rwx[3]); 90 setrwx(mode << 6, &rwx[6]); 91 setst(mode, rwx); 92 } 93 l = add_to_str(p, l, rwx); 94 return add_chr_to_str(p, l, ' '); 95 } 96 97 static size_t 98 stat_links(unsigned char **p, size_t l, struct stat *stp) 99 { 100 unsigned char lnk[64]; 101 if (!stp) 102 return add_to_str(p, l, cast_uchar " "); 103 104 sprintf(cast_char lnk, "%3ld ", (unsigned long)stp->st_nlink); 105 return add_to_str(p, l, lnk); 106 } 107 108 static int last_uid = -1; 109 static unsigned char last_user[64]; 110 111 static int last_gid = -1; 112 static unsigned char last_group[64]; 113 114 static size_t 115 stat_user(unsigned char **p, size_t l, struct stat *stp, int g) 116 { 117 struct passwd *pwd; 118 struct group *grp; 119 int id; 120 unsigned char *pp; 121 size_t i; 122 if (!stp) 123 return add_to_str(p, l, cast_uchar " "); 124 id = !g ? stp->st_uid : stp->st_gid; 125 pp = !g ? last_user : last_group; 126 if (!g && id == last_uid && last_uid != -1) 127 goto a; 128 if (g && id == last_gid && last_gid != -1) 129 goto a; 130 if (!g) { 131 ENULLLOOP(pwd, getpwuid(id)); 132 if (!pwd || !pwd->pw_name) 133 sprintf(cast_char pp, "%d", id); 134 else 135 sprintf(cast_char pp, "%.8s", pwd->pw_name); 136 last_uid = id; 137 } else { 138 ENULLLOOP(grp, getgrgid(id)); 139 if (!grp || !grp->gr_name) 140 sprintf(cast_char pp, "%d", id); 141 else 142 sprintf(cast_char pp, "%.8s", grp->gr_name); 143 last_gid = id; 144 } 145 a: 146 l = add_to_str(p, l, pp); 147 for (i = strlen(cast_const_char pp); i < 8; i++) 148 l = add_chr_to_str(p, l, ' '); 149 return add_chr_to_str(p, l, ' '); 150 } 151 152 static size_t 153 stat_size(unsigned char **p, size_t l, struct stat *stp) 154 { 155 unsigned char num[64]; 156 const int digits = 8; 157 size_t i; 158 if (!stp) 159 num[0] = 0; 160 else 161 snzprint(num, sizeof num, stp->st_size); 162 for (i = strlen(cast_const_char num); i < digits; i++) 163 l = add_chr_to_str(p, l, ' '); 164 l = add_to_str(p, l, num); 165 return add_chr_to_str(p, l, ' '); 166 } 167 168 static size_t 169 stat_date(unsigned char **p, size_t l, struct stat *stp) 170 { 171 time_t current_time; 172 time_t when; 173 struct tm *when_local; 174 unsigned char *fmt, *e; 175 unsigned char str[13]; 176 static unsigned char fmt1[] = "%b %e %Y"; 177 static unsigned char fmt2[] = "%b %e %H:%M"; 178 size_t wr; 179 EINTRLOOPX(current_time, time(NULL), (time_t)-1); 180 if (!stp) { 181 wr = 0; 182 goto set_empty; 183 } 184 when = stp->st_mtime; 185 when_local = localtime(&when); 186 if ((ulonglong)current_time 187 > (ulonglong)when + 6L * 30L * 24L * 60L * 60L 188 || (ulonglong)current_time < (ulonglong)when - 60L * 60L) 189 fmt = fmt1; 190 else 191 fmt = fmt2; 192 again: 193 wr = strftime(cast_char str, 13, cast_const_char fmt, when_local); 194 if (wr && strstr(cast_const_char str, " e ") 195 && ((e = cast_uchar strchr(cast_const_char fmt, 'e')))) { 196 *e = 'd'; 197 goto again; 198 } 199 set_empty: 200 while (wr < 12) 201 str[wr++] = ' '; 202 str[12] = 0; 203 l = add_to_str(p, l, str); 204 return add_chr_to_str(p, l, ' '); 205 } 206 207 static unsigned char * 208 get_filename(unsigned char *url) 209 { 210 unsigned char *p, *m; 211 for (p = url + 7; *p && *p != POST_CHAR; p++) 212 ; 213 m = NULL; 214 add_conv_str(&m, 0, url + 7, (int)(p - url - 7), -2); 215 return m; 216 } 217 218 struct dirs { 219 unsigned char *s; 220 unsigned char *f; 221 }; 222 223 static int 224 comp_de(const void *d1_, const void *d2_) 225 { 226 const struct dirs *d1 = (const struct dirs *)d1_; 227 const struct dirs *d2 = (const struct dirs *)d2_; 228 if (d1->f[0] == '.' && d1->f[1] == '.' && !d1->f[2]) 229 return -1; 230 if (d2->f[0] == '.' && d2->f[1] == '.' && !d2->f[2]) 231 return 1; 232 if (d1->s[0] == 'd' && d2->s[0] != 'd') 233 return -1; 234 if (d1->s[0] != 'd' && d2->s[0] == 'd') 235 return 1; 236 return strcmp(cast_const_char d1->f, cast_const_char d2->f); 237 } 238 239 void 240 file_func(struct connection *c) 241 { 242 struct cache_entry *e; 243 unsigned char *file, *name, *head = NULL; 244 int fl, flo; 245 DIR *d; 246 int h, r; 247 struct stat stt; 248 int rs; 249 if (anonymous) { 250 setcstate(c, S_BAD_URL); 251 abort_connection(c); 252 return; 253 } 254 if (!(name = get_filename(c->url))) { 255 setcstate(c, S_OUT_OF_MEM); 256 abort_connection(c); 257 return; 258 } 259 EINTRLOOP(rs, stat(cast_const_char name, &stt)); 260 if (rs) { 261 free(name); 262 setcstate(c, get_error_from_errno(errno)); 263 abort_connection(c); 264 return; 265 } 266 if (!S_ISDIR(stt.st_mode) && !S_ISREG(stt.st_mode)) { 267 free(name); 268 setcstate(c, S_FILE_TYPE); 269 abort_connection(c); 270 return; 271 } 272 h = c_open(name, O_RDONLY | O_NOCTTY); 273 if (h == -1) { 274 int er = errno; 275 d = c_opendir(name); 276 if (d) 277 goto dir; 278 free(name); 279 setcstate(c, get_error_from_errno(er)); 280 abort_connection(c); 281 return; 282 } 283 if (S_ISDIR(stt.st_mode)) { 284 struct dirs *dir; 285 int dirl; 286 int i; 287 int er; 288 struct dirent *de; 289 d = c_opendir(name); 290 er = errno; 291 EINTRLOOP(rs, close(h)); 292 if (!d) { 293 free(name); 294 setcstate(c, get_error_from_errno(er)); 295 abort_connection(c); 296 return; 297 } 298 dir: 299 dir = NULL; 300 dirl = 0; 301 if (name[0] 302 && !dir_sep(name[strlen(cast_const_char name) - 1])) { 303 if (!c->cache) { 304 if (get_connection_cache_entry(c)) { 305 free(name); 306 closedir(d); 307 setcstate(c, S_OUT_OF_MEM); 308 abort_connection(c); 309 return; 310 } 311 c->cache->refcount--; 312 } 313 e = c->cache; 314 free(e->redirect); 315 e->redirect = stracpy(c->url); 316 add_to_strn(&e->redirect, cast_uchar "/"); 317 free(name); 318 closedir(d); 319 goto end; 320 } 321 last_uid = -1; 322 last_gid = -1; 323 file = NULL; 324 fl = add_to_str(&file, 0, cast_uchar "<html><head><title>"); 325 flo = fl; 326 fl = add_conv_str(&file, fl, name, 327 (int)strlen(cast_const_char name), -1); 328 convert_file_charset(&file, &fl, flo); 329 fl = add_to_str(&file, fl, 330 cast_uchar 331 "</title></head><body><h2>Directory "); 332 flo = fl; 333 fl = add_conv_str(&file, fl, name, 334 (int)strlen(cast_const_char name), -1); 335 convert_file_charset(&file, &fl, flo); 336 fl = add_to_str(&file, fl, cast_uchar "</h2>\n<pre>"); 337 while (1) { 338 struct stat stt, *stp; 339 unsigned char **p; 340 int l; 341 unsigned char *n; 342 ENULLLOOP(de, (void *)readdir(d)); 343 if (!de) 344 break; 345 if (!strcmp(cast_const_char de->d_name, ".")) 346 continue; 347 if (!strcmp(cast_const_char de->d_name, "..")) { 348 unsigned char *n = name; 349 if (strspn(cast_const_char n, 350 dir_sep('\\') ? "/\\" : "/") 351 == strlen(cast_const_char n)) 352 continue; 353 } 354 if ((unsigned)dirl > INT_MAX / sizeof(struct dirs) - 1) 355 overalloc(); 356 dir = xrealloc(dir, (dirl + 1) * sizeof(struct dirs)); 357 dir[dirl].f = stracpy(cast_uchar de->d_name); 358 *(p = &dir[dirl++].s) = NULL; 359 l = 0; 360 n = stracpy(name); 361 add_to_strn(&n, cast_uchar de->d_name); 362 EINTRLOOP(rs, lstat(cast_const_char n, &stt)); 363 if (rs) 364 stp = NULL; 365 else 366 stp = &stt; 367 free(n); 368 l = stat_mode(p, l, stp); 369 l = stat_links(p, l, stp); 370 l = stat_user(p, l, stp, 0); 371 l = stat_user(p, l, stp, 1); 372 l = stat_size(p, l, stp); 373 l = stat_date(p, l, stp); 374 } 375 closedir(d); 376 if (dirl) 377 qsort(dir, dirl, sizeof(struct dirs), 378 (int (*)(const void *, const void *))comp_de); 379 for (i = 0; i < dirl; i++) { 380 char *lnk = NULL; 381 if (dir[i].s[0] == 'l') { 382 char *buf = NULL; 383 size_t size = 0; 384 int r; 385 char *n = (char *)stracpy(name); 386 add_to_strn((unsigned char **)&n, dir[i].f); 387 do { 388 free(buf); 389 size += ALLOC_GR; 390 if (size > INT_MAX) 391 overalloc(); 392 buf = xmalloc(size); 393 EINTRLOOP(r, readlink(n, buf, size)); 394 } while (r == size); 395 if (r == -1) 396 goto yyy; 397 buf[r] = 0; 398 lnk = buf; 399 goto xxx; 400 yyy: 401 free(buf); 402 xxx: 403 free(n); 404 } 405 fl = add_to_str(&file, fl, dir[i].s); 406 fl = add_to_str(&file, fl, cast_uchar "<a href=\"./"); 407 fl = add_conv_str(&file, fl, dir[i].f, 408 (int)strlen(cast_const_char dir[i].f), 409 1); 410 if (dir[i].s[0] == 'd') 411 fl = add_chr_to_str(&file, fl, '/'); 412 else if (lnk) { 413 struct stat st; 414 unsigned char *n = stracpy(name); 415 add_to_strn(&n, dir[i].f); 416 EINTRLOOP(rs, stat(cast_const_char n, &st)); 417 if (!rs && S_ISDIR(st.st_mode)) 418 fl = add_chr_to_str(&file, fl, '/'); 419 free(n); 420 } 421 fl = add_to_str(&file, fl, cast_uchar "\">"); 422 flo = fl; 423 fl = add_conv_str(&file, fl, dir[i].f, 424 (int)strlen(cast_const_char dir[i].f), 425 0); 426 convert_file_charset(&file, &fl, flo); 427 fl = add_to_str(&file, fl, cast_uchar "</a>"); 428 if (lnk) { 429 fl = add_to_str(&file, fl, cast_uchar " -> "); 430 fl = add_to_str(&file, fl, cast_uchar lnk); 431 free(lnk); 432 } 433 fl = add_to_str(&file, fl, cast_uchar "\n"); 434 } 435 free(name); 436 for (i = 0; i < dirl; i++) { 437 free(dir[i].s); 438 free(dir[i].f); 439 } 440 free(dir); 441 fl = add_to_str(&file, fl, cast_uchar "</pre></body></html>\n"); 442 head = stracpy(cast_uchar "\r\nContent-Type: text/html\r\n"); 443 } else { 444 free(name); 445 if (stt.st_size < 0 || stt.st_size > INT_MAX) { 446 EINTRLOOP(rs, close(h)); 447 setcstate(c, S_LARGE_FILE); 448 abort_connection(c); 449 return; 450 } 451 /* + !stt.st_size is there because of bug in Linux. Read returns 452 -EACCES when reading 0 bytes to invalid address */ 453 file = xmalloc((size_t)stt.st_size + !stt.st_size); 454 if (!file) { 455 EINTRLOOP(rs, close(h)); 456 setcstate(c, S_OUT_OF_MEM); 457 abort_connection(c); 458 return; 459 } 460 if ((r = hard_read(h, file, (int)stt.st_size)) != stt.st_size) { 461 free(file); 462 EINTRLOOP(rs, close(h)); 463 setcstate(c, r == -1 ? get_error_from_errno(errno) 464 : S_FILE_ERROR); 465 abort_connection(c); 466 return; 467 } 468 fl = r; 469 EINTRLOOP(rs, close(h)); 470 head = stracpy(cast_uchar ""); 471 } 472 if (!c->cache) { 473 if (get_connection_cache_entry(c)) { 474 free(file); 475 free(head); 476 setcstate(c, S_OUT_OF_MEM); 477 abort_connection(c); 478 return; 479 } 480 c->cache->refcount--; 481 } 482 e = c->cache; 483 free(e->head); 484 e->head = head; 485 if ((r = add_fragment(e, 0, file, fl)) < 0) { 486 free(file); 487 setcstate(c, r); 488 abort_connection(c); 489 return; 490 } 491 truncate_entry(e, fl, 1); 492 free(file); 493 end: 494 c->cache->incomplete = 0; 495 setcstate(c, S__OK); 496 abort_connection(c); 497 }