aboutsummaryrefslogtreecommitdiffhomepage
path: root/application/bookmark
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
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')
-rw-r--r--application/bookmark/BookmarkFileService.php9
-rw-r--r--application/bookmark/BookmarkIO.php35
-rw-r--r--application/bookmark/BookmarkServiceInterface.php11
3 files changed, 34 insertions, 21 deletions
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