aboutsummaryrefslogtreecommitdiffhomepage
path: root/application
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
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')
-rw-r--r--application/api/ApiMiddleware.php2
-rw-r--r--application/bookmark/BookmarkFileService.php9
-rw-r--r--application/bookmark/BookmarkIO.php35
-rw-r--r--application/bookmark/BookmarkServiceInterface.php11
-rw-r--r--application/container/ContainerBuilder.php2
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
2namespace Shaarli\Api; 2namespace Shaarli\Api;
3 3
4use malkusch\lock\mutex\FlockMutex;
4use Shaarli\Api\Exceptions\ApiAuthorizationException; 5use Shaarli\Api\Exceptions\ApiAuthorizationException;
5use Shaarli\Api\Exceptions\ApiException; 6use Shaarli\Api\Exceptions\ApiException;
6use Shaarli\Bookmark\BookmarkFileService; 7use 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
7use Exception; 7use Exception;
8use malkusch\lock\mutex\Mutex;
8use Shaarli\Bookmark\Exception\BookmarkNotFoundException; 9use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
9use Shaarli\Bookmark\Exception\DatastoreNotInitializedException; 10use Shaarli\Bookmark\Exception\DatastoreNotInitializedException;
10use Shaarli\Bookmark\Exception\EmptyDataStoreException; 11use 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
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}
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
6use Shaarli\Bookmark\Exception\BookmarkNotFoundException; 6use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
7use Shaarli\Bookmark\Exception\NotWritableDataStoreException; 7use Shaarli\Bookmark\Exception\NotWritableDataStoreException;
8use Shaarli\Config\ConfigManager;
9use Shaarli\History;
10 8
11/** 9/**
12 * Class BookmarksService 10 * Class BookmarksService
@@ -16,15 +14,6 @@ use Shaarli\History;
16interface BookmarkServiceInterface 14interface 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
5namespace Shaarli\Container; 5namespace Shaarli\Container;
6 6
7use malkusch\lock\mutex\FlockMutex;
7use Shaarli\Bookmark\BookmarkFileService; 8use Shaarli\Bookmark\BookmarkFileService;
8use Shaarli\Bookmark\BookmarkServiceInterface; 9use Shaarli\Bookmark\BookmarkServiceInterface;
9use Shaarli\Config\ConfigManager; 10use 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 };