aboutsummaryrefslogtreecommitdiffhomepage
path: root/application/bookmark/BookmarkIO.php
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2020-09-26 14:18:01 +0200
committerArthurHoaro <arthur@hoa.ro>2020-10-13 12:38:19 +0200
commitfd1ddad98df45bc3c18be7980c1cbe68ce6b219c (patch)
treee4017d3c979604f40e78cdc305f0ab191aedd4b9 /application/bookmark/BookmarkIO.php
parent458b6b9918ec27154dd45416947bb93bedb97109 (diff)
downloadShaarli-fd1ddad98df45bc3c18be7980c1cbe68ce6b219c.tar.gz
Shaarli-fd1ddad98df45bc3c18be7980c1cbe68ce6b219c.tar.zst
Shaarli-fd1ddad98df45bc3c18be7980c1cbe68ce6b219c.zip
Add mutex on datastore I/O operations
To make sure that there is no concurrent operation on the datastore file. Fixes #1132
Diffstat (limited to 'application/bookmark/BookmarkIO.php')
-rw-r--r--application/bookmark/BookmarkIO.php35
1 files changed, 27 insertions, 8 deletions
diff --git a/application/bookmark/BookmarkIO.php b/application/bookmark/BookmarkIO.php
index 6bf7f365..099653b0 100644
--- a/application/bookmark/BookmarkIO.php
+++ b/application/bookmark/BookmarkIO.php
@@ -2,6 +2,8 @@
2 2
3namespace Shaarli\Bookmark; 3namespace Shaarli\Bookmark;
4 4
5use malkusch\lock\mutex\Mutex;
6use malkusch\lock\mutex\NoMutex;
5use Shaarli\Bookmark\Exception\DatastoreNotInitializedException; 7use Shaarli\Bookmark\Exception\DatastoreNotInitializedException;
6use Shaarli\Bookmark\Exception\EmptyDataStoreException; 8use Shaarli\Bookmark\Exception\EmptyDataStoreException;
7use Shaarli\Bookmark\Exception\NotWritableDataStoreException; 9use Shaarli\Bookmark\Exception\NotWritableDataStoreException;
@@ -27,11 +29,14 @@ class BookmarkIO
27 */ 29 */
28 protected $conf; 30 protected $conf;
29 31
32
33 /** @var Mutex */
34 protected $mutex;
35
30 /** 36 /**
31 * string Datastore PHP prefix 37 * string Datastore PHP prefix
32 */ 38 */
33 protected static $phpPrefix = '<?php /* '; 39 protected static $phpPrefix = '<?php /* ';
34
35 /** 40 /**
36 * string Datastore PHP suffix 41 * string Datastore PHP suffix
37 */ 42 */
@@ -42,10 +47,15 @@ class BookmarkIO
42 * 47 *
43 * @param ConfigManager $conf instance 48 * @param ConfigManager $conf instance
44 */ 49 */
45 public function __construct($conf) 50 public function __construct(ConfigManager $conf, Mutex $mutex = null)
46 { 51 {
52 if ($mutex === null) {
53 // This should only happen with legacy classes
54 $mutex = new NoMutex();
55 }
47 $this->conf = $conf; 56 $this->conf = $conf;
48 $this->datastore = $conf->get('resource.datastore'); 57 $this->datastore = $conf->get('resource.datastore');
58 $this->mutex = $mutex;
49 } 59 }
50 60
51 /** 61 /**
@@ -67,11 +77,16 @@ class BookmarkIO
67 throw new NotWritableDataStoreException($this->datastore); 77 throw new NotWritableDataStoreException($this->datastore);
68 } 78 }
69 79
80 $content = null;
81 $this->mutex->synchronized(function () use (&$content) {
82 $content = file_get_contents($this->datastore);
83 });
84
70 // Note that gzinflate is faster than gzuncompress. 85 // Note that gzinflate is faster than gzuncompress.
71 // See: http://www.php.net/manual/en/function.gzdeflate.php#96439 86 // See: http://www.php.net/manual/en/function.gzdeflate.php#96439
72 $links = unserialize(gzinflate(base64_decode( 87 $links = unserialize(gzinflate(base64_decode(
73 substr(file_get_contents($this->datastore), 88 substr($content, strlen(self::$phpPrefix), -strlen(self::$phpSuffix))
74 strlen(self::$phpPrefix), -strlen(self::$phpSuffix))))); 89 )));
75 90
76 if (empty($links)) { 91 if (empty($links)) {
77 if (filesize($this->datastore) > 100) { 92 if (filesize($this->datastore) > 100) {
@@ -100,9 +115,13 @@ class BookmarkIO
100 throw new NotWritableDataStoreException(dirname($this->datastore)); 115 throw new NotWritableDataStoreException(dirname($this->datastore));
101 } 116 }
102 117
103 file_put_contents( 118 $data = self::$phpPrefix.base64_encode(gzdeflate(serialize($links))).self::$phpSuffix;
104 $this->datastore, 119
105 self::$phpPrefix.base64_encode(gzdeflate(serialize($links))).self::$phpSuffix 120 $this->mutex->synchronized(function () use ($data) {
106 ); 121 file_put_contents(
122 $this->datastore,
123 $data
124 );
125 });
107 } 126 }
108} 127}