@@ -24,14 +24,14 @@ char *sha1_pack_index_name(const unsigned char *sha1)
24
24
return odb_pack_name (& buf , sha1 , "idx" );
25
25
}
26
26
27
- unsigned int pack_used_ctr ;
28
- unsigned int pack_mmap_calls ;
29
- unsigned int peak_pack_open_windows ;
30
- unsigned int pack_open_windows ;
27
+ static unsigned int pack_used_ctr ;
28
+ static unsigned int pack_mmap_calls ;
29
+ static unsigned int peak_pack_open_windows ;
30
+ static unsigned int pack_open_windows ;
31
31
unsigned int pack_open_fds ;
32
- unsigned int pack_max_fds ;
33
- size_t peak_pack_mapped ;
34
- size_t pack_mapped ;
32
+ static unsigned int pack_max_fds ;
33
+ static size_t peak_pack_mapped ;
34
+ static size_t pack_mapped ;
35
35
struct packed_git * packed_git ;
36
36
37
37
static struct mru packed_git_mru_storage ;
@@ -228,7 +228,7 @@ static void scan_windows(struct packed_git *p,
228
228
}
229
229
}
230
230
231
- int unuse_one_window (struct packed_git * current )
231
+ static int unuse_one_window (struct packed_git * current )
232
232
{
233
233
struct packed_git * p , * lru_p = NULL ;
234
234
struct pack_window * lru_w = NULL , * lru_l = NULL ;
@@ -274,7 +274,7 @@ void close_pack_windows(struct packed_git *p)
274
274
}
275
275
}
276
276
277
- int close_pack_fd (struct packed_git * p )
277
+ static int close_pack_fd (struct packed_git * p )
278
278
{
279
279
if (p -> pack_fd < 0 )
280
280
return 0 ;
@@ -311,3 +311,288 @@ void close_all_packs(void)
311
311
else
312
312
close_pack (p );
313
313
}
314
+
315
+ /*
316
+ * The LRU pack is the one with the oldest MRU window, preferring packs
317
+ * with no used windows, or the oldest mtime if it has no windows allocated.
318
+ */
319
+ static void find_lru_pack (struct packed_git * p , struct packed_git * * lru_p , struct pack_window * * mru_w , int * accept_windows_inuse )
320
+ {
321
+ struct pack_window * w , * this_mru_w ;
322
+ int has_windows_inuse = 0 ;
323
+
324
+ /*
325
+ * Reject this pack if it has windows and the previously selected
326
+ * one does not. If this pack does not have windows, reject
327
+ * it if the pack file is newer than the previously selected one.
328
+ */
329
+ if (* lru_p && !* mru_w && (p -> windows || p -> mtime > (* lru_p )-> mtime ))
330
+ return ;
331
+
332
+ for (w = this_mru_w = p -> windows ; w ; w = w -> next ) {
333
+ /*
334
+ * Reject this pack if any of its windows are in use,
335
+ * but the previously selected pack did not have any
336
+ * inuse windows. Otherwise, record that this pack
337
+ * has windows in use.
338
+ */
339
+ if (w -> inuse_cnt ) {
340
+ if (* accept_windows_inuse )
341
+ has_windows_inuse = 1 ;
342
+ else
343
+ return ;
344
+ }
345
+
346
+ if (w -> last_used > this_mru_w -> last_used )
347
+ this_mru_w = w ;
348
+
349
+ /*
350
+ * Reject this pack if it has windows that have been
351
+ * used more recently than the previously selected pack.
352
+ * If the previously selected pack had windows inuse and
353
+ * we have not encountered a window in this pack that is
354
+ * inuse, skip this check since we prefer a pack with no
355
+ * inuse windows to one that has inuse windows.
356
+ */
357
+ if (* mru_w && * accept_windows_inuse == has_windows_inuse &&
358
+ this_mru_w -> last_used > (* mru_w )-> last_used )
359
+ return ;
360
+ }
361
+
362
+ /*
363
+ * Select this pack.
364
+ */
365
+ * mru_w = this_mru_w ;
366
+ * lru_p = p ;
367
+ * accept_windows_inuse = has_windows_inuse ;
368
+ }
369
+
370
+ static int close_one_pack (void )
371
+ {
372
+ struct packed_git * p , * lru_p = NULL ;
373
+ struct pack_window * mru_w = NULL ;
374
+ int accept_windows_inuse = 1 ;
375
+
376
+ for (p = packed_git ; p ; p = p -> next ) {
377
+ if (p -> pack_fd == -1 )
378
+ continue ;
379
+ find_lru_pack (p , & lru_p , & mru_w , & accept_windows_inuse );
380
+ }
381
+
382
+ if (lru_p )
383
+ return close_pack_fd (lru_p );
384
+
385
+ return 0 ;
386
+ }
387
+
388
+ static unsigned int get_max_fd_limit (void )
389
+ {
390
+ #ifdef RLIMIT_NOFILE
391
+ {
392
+ struct rlimit lim ;
393
+
394
+ if (!getrlimit (RLIMIT_NOFILE , & lim ))
395
+ return lim .rlim_cur ;
396
+ }
397
+ #endif
398
+
399
+ #ifdef _SC_OPEN_MAX
400
+ {
401
+ long open_max = sysconf (_SC_OPEN_MAX );
402
+ if (0 < open_max )
403
+ return open_max ;
404
+ /*
405
+ * Otherwise, we got -1 for one of the two
406
+ * reasons:
407
+ *
408
+ * (1) sysconf() did not understand _SC_OPEN_MAX
409
+ * and signaled an error with -1; or
410
+ * (2) sysconf() said there is no limit.
411
+ *
412
+ * We _could_ clear errno before calling sysconf() to
413
+ * tell these two cases apart and return a huge number
414
+ * in the latter case to let the caller cap it to a
415
+ * value that is not so selfish, but letting the
416
+ * fallback OPEN_MAX codepath take care of these cases
417
+ * is a lot simpler.
418
+ */
419
+ }
420
+ #endif
421
+
422
+ #ifdef OPEN_MAX
423
+ return OPEN_MAX ;
424
+ #else
425
+ return 1 ; /* see the caller ;-) */
426
+ #endif
427
+ }
428
+
429
+ /*
430
+ * Do not call this directly as this leaks p->pack_fd on error return;
431
+ * call open_packed_git() instead.
432
+ */
433
+ static int open_packed_git_1 (struct packed_git * p )
434
+ {
435
+ struct stat st ;
436
+ struct pack_header hdr ;
437
+ unsigned char sha1 [20 ];
438
+ unsigned char * idx_sha1 ;
439
+ long fd_flag ;
440
+
441
+ if (!p -> index_data && open_pack_index (p ))
442
+ return error ("packfile %s index unavailable" , p -> pack_name );
443
+
444
+ if (!pack_max_fds ) {
445
+ unsigned int max_fds = get_max_fd_limit ();
446
+
447
+ /* Save 3 for stdin/stdout/stderr, 22 for work */
448
+ if (25 < max_fds )
449
+ pack_max_fds = max_fds - 25 ;
450
+ else
451
+ pack_max_fds = 1 ;
452
+ }
453
+
454
+ while (pack_max_fds <= pack_open_fds && close_one_pack ())
455
+ ; /* nothing */
456
+
457
+ p -> pack_fd = git_open (p -> pack_name );
458
+ if (p -> pack_fd < 0 || fstat (p -> pack_fd , & st ))
459
+ return -1 ;
460
+ pack_open_fds ++ ;
461
+
462
+ /* If we created the struct before we had the pack we lack size. */
463
+ if (!p -> pack_size ) {
464
+ if (!S_ISREG (st .st_mode ))
465
+ return error ("packfile %s not a regular file" , p -> pack_name );
466
+ p -> pack_size = st .st_size ;
467
+ } else if (p -> pack_size != st .st_size )
468
+ return error ("packfile %s size changed" , p -> pack_name );
469
+
470
+ /* We leave these file descriptors open with sliding mmap;
471
+ * there is no point keeping them open across exec(), though.
472
+ */
473
+ fd_flag = fcntl (p -> pack_fd , F_GETFD , 0 );
474
+ if (fd_flag < 0 )
475
+ return error ("cannot determine file descriptor flags" );
476
+ fd_flag |= FD_CLOEXEC ;
477
+ if (fcntl (p -> pack_fd , F_SETFD , fd_flag ) == -1 )
478
+ return error ("cannot set FD_CLOEXEC" );
479
+
480
+ /* Verify we recognize this pack file format. */
481
+ if (read_in_full (p -> pack_fd , & hdr , sizeof (hdr )) != sizeof (hdr ))
482
+ return error ("file %s is far too short to be a packfile" , p -> pack_name );
483
+ if (hdr .hdr_signature != htonl (PACK_SIGNATURE ))
484
+ return error ("file %s is not a GIT packfile" , p -> pack_name );
485
+ if (!pack_version_ok (hdr .hdr_version ))
486
+ return error ("packfile %s is version %" PRIu32 " and not"
487
+ " supported (try upgrading GIT to a newer version)" ,
488
+ p -> pack_name , ntohl (hdr .hdr_version ));
489
+
490
+ /* Verify the pack matches its index. */
491
+ if (p -> num_objects != ntohl (hdr .hdr_entries ))
492
+ return error ("packfile %s claims to have %" PRIu32 " objects"
493
+ " while index indicates %" PRIu32 " objects" ,
494
+ p -> pack_name , ntohl (hdr .hdr_entries ),
495
+ p -> num_objects );
496
+ if (lseek (p -> pack_fd , p -> pack_size - sizeof (sha1 ), SEEK_SET ) == -1 )
497
+ return error ("end of packfile %s is unavailable" , p -> pack_name );
498
+ if (read_in_full (p -> pack_fd , sha1 , sizeof (sha1 )) != sizeof (sha1 ))
499
+ return error ("packfile %s signature is unavailable" , p -> pack_name );
500
+ idx_sha1 = ((unsigned char * )p -> index_data ) + p -> index_size - 40 ;
501
+ if (hashcmp (sha1 , idx_sha1 ))
502
+ return error ("packfile %s does not match index" , p -> pack_name );
503
+ return 0 ;
504
+ }
505
+
506
+ int open_packed_git (struct packed_git * p )
507
+ {
508
+ if (!open_packed_git_1 (p ))
509
+ return 0 ;
510
+ close_pack_fd (p );
511
+ return -1 ;
512
+ }
513
+
514
+ static int in_window (struct pack_window * win , off_t offset )
515
+ {
516
+ /* We must promise at least 20 bytes (one hash) after the
517
+ * offset is available from this window, otherwise the offset
518
+ * is not actually in this window and a different window (which
519
+ * has that one hash excess) must be used. This is to support
520
+ * the object header and delta base parsing routines below.
521
+ */
522
+ off_t win_off = win -> offset ;
523
+ return win_off <= offset
524
+ && (offset + 20 ) <= (win_off + win -> len );
525
+ }
526
+
527
+ unsigned char * use_pack (struct packed_git * p ,
528
+ struct pack_window * * w_cursor ,
529
+ off_t offset ,
530
+ unsigned long * left )
531
+ {
532
+ struct pack_window * win = * w_cursor ;
533
+
534
+ /* Since packfiles end in a hash of their content and it's
535
+ * pointless to ask for an offset into the middle of that
536
+ * hash, and the in_window function above wouldn't match
537
+ * don't allow an offset too close to the end of the file.
538
+ */
539
+ if (!p -> pack_size && p -> pack_fd == -1 && open_packed_git (p ))
540
+ die ("packfile %s cannot be accessed" , p -> pack_name );
541
+ if (offset > (p -> pack_size - 20 ))
542
+ die ("offset beyond end of packfile (truncated pack?)" );
543
+ if (offset < 0 )
544
+ die (_ ("offset before end of packfile (broken .idx?)" ));
545
+
546
+ if (!win || !in_window (win , offset )) {
547
+ if (win )
548
+ win -> inuse_cnt -- ;
549
+ for (win = p -> windows ; win ; win = win -> next ) {
550
+ if (in_window (win , offset ))
551
+ break ;
552
+ }
553
+ if (!win ) {
554
+ size_t window_align = packed_git_window_size / 2 ;
555
+ off_t len ;
556
+
557
+ if (p -> pack_fd == -1 && open_packed_git (p ))
558
+ die ("packfile %s cannot be accessed" , p -> pack_name );
559
+
560
+ win = xcalloc (1 , sizeof (* win ));
561
+ win -> offset = (offset / window_align ) * window_align ;
562
+ len = p -> pack_size - win -> offset ;
563
+ if (len > packed_git_window_size )
564
+ len = packed_git_window_size ;
565
+ win -> len = (size_t )len ;
566
+ pack_mapped += win -> len ;
567
+ while (packed_git_limit < pack_mapped
568
+ && unuse_one_window (p ))
569
+ ; /* nothing */
570
+ win -> base = xmmap (NULL , win -> len ,
571
+ PROT_READ , MAP_PRIVATE ,
572
+ p -> pack_fd , win -> offset );
573
+ if (win -> base == MAP_FAILED )
574
+ die_errno ("packfile %s cannot be mapped" ,
575
+ p -> pack_name );
576
+ if (!win -> offset && win -> len == p -> pack_size
577
+ && !p -> do_not_close )
578
+ close_pack_fd (p );
579
+ pack_mmap_calls ++ ;
580
+ pack_open_windows ++ ;
581
+ if (pack_mapped > peak_pack_mapped )
582
+ peak_pack_mapped = pack_mapped ;
583
+ if (pack_open_windows > peak_pack_open_windows )
584
+ peak_pack_open_windows = pack_open_windows ;
585
+ win -> next = p -> windows ;
586
+ p -> windows = win ;
587
+ }
588
+ }
589
+ if (win != * w_cursor ) {
590
+ win -> last_used = pack_used_ctr ++ ;
591
+ win -> inuse_cnt ++ ;
592
+ * w_cursor = win ;
593
+ }
594
+ offset -= win -> offset ;
595
+ if (left )
596
+ * left = win -> len - xsize_t (offset );
597
+ return win -> base + offset ;
598
+ }
0 commit comments