diff options
author | ArthurHoaro <arthur@hoa.ro> | 2020-09-26 14:18:01 +0200 |
---|---|---|
committer | ArthurHoaro <arthur@hoa.ro> | 2020-10-13 12:38:19 +0200 |
commit | fd1ddad98df45bc3c18be7980c1cbe68ce6b219c (patch) | |
tree | e4017d3c979604f40e78cdc305f0ab191aedd4b9 /application | |
parent | 458b6b9918ec27154dd45416947bb93bedb97109 (diff) | |
download | Shaarli-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')
-rw-r--r-- | application/api/ApiMiddleware.php | 2 | ||||
-rw-r--r-- | application/bookmark/BookmarkFileService.php | 9 | ||||
-rw-r--r-- | application/bookmark/BookmarkIO.php | 35 | ||||
-rw-r--r-- | application/bookmark/BookmarkServiceInterface.php | 11 | ||||
-rw-r--r-- | application/container/ContainerBuilder.php | 2 |
5 files changed, 38 insertions, 21 deletions
diff --git a/application/api/ApiMiddleware.php b/application/api/ApiMiddleware.php index f5b53b01..adc8b266 100644 --- a/application/api/ApiMiddleware.php +++ b/application/api/ApiMiddleware.php | |||
@@ -1,6 +1,7 @@ | |||
1 | <?php | 1 | <?php |
2 | namespace Shaarli\Api; | 2 | namespace Shaarli\Api; |
3 | 3 | ||
4 | use malkusch\lock\mutex\FlockMutex; | ||
4 | use Shaarli\Api\Exceptions\ApiAuthorizationException; | 5 | use Shaarli\Api\Exceptions\ApiAuthorizationException; |
5 | use Shaarli\Api\Exceptions\ApiException; | 6 | use Shaarli\Api\Exceptions\ApiException; |
6 | use Shaarli\Bookmark\BookmarkFileService; | 7 | use Shaarli\Bookmark\BookmarkFileService; |
@@ -143,6 +144,7 @@ class ApiMiddleware | |||
143 | $linkDb = new BookmarkFileService( | 144 | $linkDb = new BookmarkFileService( |
144 | $conf, | 145 | $conf, |
145 | $this->container->get('history'), | 146 | $this->container->get('history'), |
147 | new FlockMutex(fopen(SHAARLI_MUTEX_FILE, 'r'), 2), | ||
146 | true | 148 | true |
147 | ); | 149 | ); |
148 | $this->container['db'] = $linkDb; | 150 | $this->container['db'] = $linkDb; |
diff --git a/application/bookmark/BookmarkFileService.php b/application/bookmark/BookmarkFileService.php index c9ec2609..1ba00712 100644 --- a/application/bookmark/BookmarkFileService.php +++ b/application/bookmark/BookmarkFileService.php | |||
@@ -5,6 +5,7 @@ namespace Shaarli\Bookmark; | |||
5 | 5 | ||
6 | 6 | ||
7 | use Exception; | 7 | use Exception; |
8 | use malkusch\lock\mutex\Mutex; | ||
8 | use Shaarli\Bookmark\Exception\BookmarkNotFoundException; | 9 | use Shaarli\Bookmark\Exception\BookmarkNotFoundException; |
9 | use Shaarli\Bookmark\Exception\DatastoreNotInitializedException; | 10 | use Shaarli\Bookmark\Exception\DatastoreNotInitializedException; |
10 | use Shaarli\Bookmark\Exception\EmptyDataStoreException; | 11 | use Shaarli\Bookmark\Exception\EmptyDataStoreException; |
@@ -47,15 +48,19 @@ class BookmarkFileService implements BookmarkServiceInterface | |||
47 | /** @var bool true for logged in users. Default value to retrieve private bookmarks. */ | 48 | /** @var bool true for logged in users. Default value to retrieve private bookmarks. */ |
48 | protected $isLoggedIn; | 49 | protected $isLoggedIn; |
49 | 50 | ||
51 | /** @var Mutex */ | ||
52 | protected $mutex; | ||
53 | |||
50 | /** | 54 | /** |
51 | * @inheritDoc | 55 | * @inheritDoc |
52 | */ | 56 | */ |
53 | public function __construct(ConfigManager $conf, History $history, $isLoggedIn) | 57 | public function __construct(ConfigManager $conf, History $history, Mutex $mutex, $isLoggedIn) |
54 | { | 58 | { |
55 | $this->conf = $conf; | 59 | $this->conf = $conf; |
56 | $this->history = $history; | 60 | $this->history = $history; |
61 | $this->mutex = $mutex; | ||
57 | $this->pageCacheManager = new PageCacheManager($this->conf->get('resource.page_cache'), $isLoggedIn); | 62 | $this->pageCacheManager = new PageCacheManager($this->conf->get('resource.page_cache'), $isLoggedIn); |
58 | $this->bookmarksIO = new BookmarkIO($this->conf); | 63 | $this->bookmarksIO = new BookmarkIO($this->conf, $this->mutex); |
59 | $this->isLoggedIn = $isLoggedIn; | 64 | $this->isLoggedIn = $isLoggedIn; |
60 | 65 | ||
61 | if (!$this->isLoggedIn && $this->conf->get('privacy.hide_public_links', false)) { | 66 | if (!$this->isLoggedIn && $this->conf->get('privacy.hide_public_links', false)) { |
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 | ||
3 | namespace Shaarli\Bookmark; | 3 | namespace Shaarli\Bookmark; |
4 | 4 | ||
5 | use malkusch\lock\mutex\Mutex; | ||
6 | use malkusch\lock\mutex\NoMutex; | ||
5 | use Shaarli\Bookmark\Exception\DatastoreNotInitializedException; | 7 | use Shaarli\Bookmark\Exception\DatastoreNotInitializedException; |
6 | use Shaarli\Bookmark\Exception\EmptyDataStoreException; | 8 | use Shaarli\Bookmark\Exception\EmptyDataStoreException; |
7 | use Shaarli\Bookmark\Exception\NotWritableDataStoreException; | 9 | use 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 | } |
diff --git a/application/bookmark/BookmarkServiceInterface.php b/application/bookmark/BookmarkServiceInterface.php index b9b483eb..638cfa5f 100644 --- a/application/bookmark/BookmarkServiceInterface.php +++ b/application/bookmark/BookmarkServiceInterface.php | |||
@@ -5,8 +5,6 @@ namespace Shaarli\Bookmark; | |||
5 | 5 | ||
6 | use Shaarli\Bookmark\Exception\BookmarkNotFoundException; | 6 | use Shaarli\Bookmark\Exception\BookmarkNotFoundException; |
7 | use Shaarli\Bookmark\Exception\NotWritableDataStoreException; | 7 | use Shaarli\Bookmark\Exception\NotWritableDataStoreException; |
8 | use Shaarli\Config\ConfigManager; | ||
9 | use Shaarli\History; | ||
10 | 8 | ||
11 | /** | 9 | /** |
12 | * Class BookmarksService | 10 | * Class BookmarksService |
@@ -16,15 +14,6 @@ use Shaarli\History; | |||
16 | interface BookmarkServiceInterface | 14 | interface BookmarkServiceInterface |
17 | { | 15 | { |
18 | /** | 16 | /** |
19 | * BookmarksService constructor. | ||
20 | * | ||
21 | * @param ConfigManager $conf instance | ||
22 | * @param History $history instance | ||
23 | * @param bool $isLoggedIn true if the current user is logged in | ||
24 | */ | ||
25 | public function __construct(ConfigManager $conf, History $history, $isLoggedIn); | ||
26 | |||
27 | /** | ||
28 | * Find a bookmark by hash | 17 | * Find a bookmark by hash |
29 | * | 18 | * |
30 | * @param string $hash | 19 | * @param string $hash |
diff --git a/application/container/ContainerBuilder.php b/application/container/ContainerBuilder.php index 55bb51b5..c21d58dd 100644 --- a/application/container/ContainerBuilder.php +++ b/application/container/ContainerBuilder.php | |||
@@ -4,6 +4,7 @@ declare(strict_types=1); | |||
4 | 4 | ||
5 | namespace Shaarli\Container; | 5 | namespace Shaarli\Container; |
6 | 6 | ||
7 | use malkusch\lock\mutex\FlockMutex; | ||
7 | use Shaarli\Bookmark\BookmarkFileService; | 8 | use Shaarli\Bookmark\BookmarkFileService; |
8 | use Shaarli\Bookmark\BookmarkServiceInterface; | 9 | use Shaarli\Bookmark\BookmarkServiceInterface; |
9 | use Shaarli\Config\ConfigManager; | 10 | use Shaarli\Config\ConfigManager; |
@@ -84,6 +85,7 @@ class ContainerBuilder | |||
84 | return new BookmarkFileService( | 85 | return new BookmarkFileService( |
85 | $container->conf, | 86 | $container->conf, |
86 | $container->history, | 87 | $container->history, |
88 | new FlockMutex(fopen(SHAARLI_MUTEX_FILE, 'r'), 2), | ||
87 | $container->loginManager->isLoggedIn() | 89 | $container->loginManager->isLoggedIn() |
88 | ); | 90 | ); |
89 | }; | 91 | }; |