sizelimitwriter.go 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. /*
  2. * Copyright (C)2013-2020 ZeroTier, Inc.
  3. *
  4. * Use of this software is governed by the Business Source License included
  5. * in the LICENSE.TXT file in the project's root directory.
  6. *
  7. * Change Date: 2025-01-01
  8. *
  9. * On the date above, in accordance with the Business Source License, use
  10. * of this software will be governed by version 2.0 of the Apache License.
  11. */
  12. /****/
  13. package zerotier
  14. import (
  15. "io"
  16. "os"
  17. "sync"
  18. )
  19. type sizeLimitWriter struct {
  20. f *os.File
  21. l sync.Mutex
  22. }
  23. func sizeLimitWriterOpen(p string) (*sizeLimitWriter, error) {
  24. f, err := os.OpenFile(p, os.O_CREATE|os.O_RDWR, 0644)
  25. if err != nil {
  26. return nil, err
  27. }
  28. _, _ = f.Seek(0, io.SeekEnd)
  29. return &sizeLimitWriter{f: f}, nil
  30. }
  31. // Write implements io.Writer
  32. func (w *sizeLimitWriter) Write(b []byte) (int, error) {
  33. w.l.Lock()
  34. defer w.l.Unlock()
  35. return w.f.Write(b)
  36. }
  37. // Close closes the underlying file
  38. func (w *sizeLimitWriter) Close() error {
  39. w.l.Lock()
  40. defer w.l.Unlock()
  41. return w.f.Close()
  42. }
  43. func (w *sizeLimitWriter) trim(maxSize int, trimFactor float64, trimAtCR bool) error {
  44. w.l.Lock()
  45. defer w.l.Unlock()
  46. flen, err := w.f.Seek(0, io.SeekEnd)
  47. if err != nil {
  48. return err
  49. }
  50. if flen > int64(maxSize) {
  51. var buf [131072]byte
  52. trimAt := int64(float64(maxSize) * trimFactor)
  53. if trimAt >= flen { // sanity check
  54. return nil
  55. }
  56. if trimAtCR {
  57. lookForCR:
  58. for {
  59. nr, err := w.f.ReadAt(buf[0:1024], trimAt)
  60. if err != nil {
  61. return err
  62. }
  63. for i := 0; i < nr; i++ {
  64. trimAt++
  65. if buf[i] == byte('\n') {
  66. break lookForCR
  67. }
  68. }
  69. if trimAt >= flen {
  70. return nil
  71. }
  72. }
  73. }
  74. copyTo := int64(0)
  75. for trimAt < flen {
  76. nr, _ := w.f.ReadAt(buf[:], trimAt)
  77. if nr > 0 {
  78. wr, _ := w.f.WriteAt(buf[0:nr], copyTo)
  79. if wr > 0 {
  80. copyTo += int64(wr)
  81. } else {
  82. break
  83. }
  84. } else {
  85. break
  86. }
  87. }
  88. err = w.f.Truncate(copyTo)
  89. if err != nil {
  90. return err
  91. }
  92. _, err = w.f.Seek(0, io.SeekEnd)
  93. return err
  94. }
  95. return nil
  96. }