lockbook_server_lib/
file_service.rs

1use crate::billing::app_store_client::AppStoreClient;
2use crate::billing::google_play_client::GooglePlayClient;
3use crate::billing::stripe_client::StripeClient;
4use crate::document_service::DocumentService;
5use crate::file_service::UpsertError::UsageIsOverDataCap;
6use crate::schema::ServerDb;
7use crate::ServerError;
8use crate::ServerError::ClientError;
9
10use crate::{RequestContext, ServerState};
11use db_rs::Db;
12use lockbook_shared::api::*;
13use lockbook_shared::clock::get_time;
14use lockbook_shared::file_like::FileLike;
15use lockbook_shared::file_metadata::{Diff, Owner};
16use lockbook_shared::server_file::{IntoServerFile, ServerFile};
17use lockbook_shared::server_tree::ServerTree;
18use lockbook_shared::tree_like::TreeLike;
19use lockbook_shared::{SharedErrorKind, SharedResult};
20use std::collections::{HashMap, HashSet};
21use std::hash::Hash;
22use std::ops::DerefMut;
23use tracing::{debug, error, warn};
24
25impl<S, A, G, D> ServerState<S, A, G, D>
26where
27    S: StripeClient,
28    A: AppStoreClient,
29    G: GooglePlayClient,
30    D: DocumentService,
31{
32    pub async fn upsert_file_metadata(
33        &self, context: RequestContext<UpsertRequest>,
34    ) -> Result<(), ServerError<UpsertError>> {
35        let request = context.request;
36        let req_owner = Owner(context.public_key);
37
38        let mut new_deleted = vec![];
39        {
40            let mut prior_deleted = HashSet::new();
41            let mut current_deleted = HashSet::new();
42
43            let mut lock = self.index_db.lock()?;
44            let db = lock.deref_mut();
45            let tx = db.begin_transaction()?;
46
47            let usage_cap =
48                Self::get_cap(db, &context.public_key).map_err(|err| internal!("{:?}", err))?;
49
50            let mut tree = ServerTree::new(
51                req_owner,
52                &mut db.owned_files,
53                &mut db.shared_files,
54                &mut db.file_children,
55                &mut db.metas,
56            )?
57            .to_lazy();
58
59            let old_usage = Self::get_usage_helper(&mut tree, db.sizes.get())
60                .map_err(|err| internal!("{:?}", err))?
61                .iter()
62                .map(|f| f.size_bytes)
63                .sum::<u64>();
64
65            for id in tree.owned_ids() {
66                if tree.calculate_deleted(&id)? {
67                    prior_deleted.insert(id);
68                }
69            }
70
71            let mut tree = tree.stage_diff(request.updates.clone())?;
72            for id in tree.owned_ids() {
73                if tree.calculate_deleted(&id)? {
74                    current_deleted.insert(id);
75                }
76            }
77
78            tree.validate(req_owner)?;
79
80            let new_usage = Self::get_usage_helper(&mut tree, db.sizes.get())
81                .map_err(|err| internal!("{:?}", err))?
82                .iter()
83                .map(|f| f.size_bytes)
84                .sum::<u64>();
85
86            debug!(?old_usage, ?new_usage, ?usage_cap, "usage caps on upsert");
87
88            if new_usage > usage_cap && new_usage >= old_usage {
89                return Err(ClientError(UsageIsOverDataCap));
90            }
91
92            let tree = tree.promote()?;
93
94            for id in tree.owned_ids() {
95                if tree.find(&id)?.is_document()
96                    && current_deleted.contains(&id)
97                    && !prior_deleted.contains(&id)
98                {
99                    let meta = tree.find(&id)?;
100                    if let Some(hmac) = meta.file.timestamped_value.value.document_hmac {
101                        db.sizes.remove(meta.id())?;
102                        new_deleted.push((*meta.id(), hmac));
103                    }
104                }
105            }
106
107            let all_files: Vec<ServerFile> = tree.all_files()?.into_iter().cloned().collect();
108            for meta in all_files {
109                let id = meta.id();
110                if current_deleted.contains(id) && !prior_deleted.contains(id) {
111                    for user_access_info in meta.user_access_keys() {
112                        db.shared_files
113                            .remove(&Owner(user_access_info.encrypted_for), id)?;
114                    }
115                }
116            }
117
118            db.last_seen.insert(req_owner, get_time().0 as u64)?;
119
120            tx.drop_safely()?;
121        }
122
123        for update in request.updates {
124            let new = update.new;
125            let id = *new.id();
126            match update.old {
127                None => {
128                    debug!(?id, "Created file");
129                }
130                Some(old) => {
131                    let old_parent = *old.parent();
132                    let new_parent = *new.parent();
133                    if old.parent() != new.parent() {
134                        debug!(?id, ?old_parent, ?new_parent, "Moved file");
135                    }
136                    if old.secret_name() != new.secret_name() {
137                        debug!(?id, "Renamed file");
138                    }
139                    if old.owner() != new.owner() {
140                        debug!(?id, ?old_parent, ?new_parent, "Changed owner for file");
141                    }
142                    if old.explicitly_deleted() != new.explicitly_deleted() {
143                        debug!(?id, "Deleted file");
144                    }
145                    if old.user_access_keys() != new.user_access_keys() {
146                        let all_sharees: Vec<_> = old
147                            .user_access_keys()
148                            .iter()
149                            .chain(new.user_access_keys().iter())
150                            .map(|k| Owner(k.encrypted_for))
151                            .collect();
152                        for sharee in all_sharees {
153                            let new = if let Some(k) = new
154                                .user_access_keys()
155                                .iter()
156                                .find(|k| k.encrypted_for == sharee.0)
157                            {
158                                k
159                            } else {
160                                debug!(?id, ?sharee, "Disappeared user access key");
161                                continue;
162                            };
163                            let old = if let Some(k) = old
164                                .user_access_keys()
165                                .iter()
166                                .find(|k| k.encrypted_for == sharee.0)
167                            {
168                                k
169                            } else {
170                                debug!(?id, ?sharee, ?new.mode, "Added user access key");
171                                continue;
172                            };
173                            if old.mode != new.mode {
174                                debug!(?id, ?sharee, ?old.mode, ?new.mode, "Modified user access mode");
175                            }
176                            if old.deleted != new.deleted {
177                                debug!(?id, ?sharee, ?old.deleted, ?new.deleted, "Deleted user access key");
178                            }
179                        }
180                    }
181                }
182            }
183        }
184
185        for (id, hmac) in new_deleted {
186            self.document_service.delete(&id, &hmac).await?;
187            let hmac = base64::encode_config(hmac, base64::URL_SAFE);
188            debug!(?id, ?hmac, "Deleted document contents");
189        }
190        Ok(())
191    }
192
193    pub async fn change_doc(
194        &self, context: RequestContext<ChangeDocRequest>,
195    ) -> Result<(), ServerError<ChangeDocError>> {
196        use ChangeDocError::*;
197
198        let request = context.request;
199        let owner = Owner(context.public_key);
200        let id = *request.diff.id();
201
202        // Validate Diff
203        if request.diff.diff() != vec![Diff::Hmac] {
204            return Err(ClientError(DiffMalformed));
205        }
206        let hmac = if let Some(hmac) = request.diff.new.document_hmac() {
207            base64::encode_config(hmac, base64::URL_SAFE)
208        } else {
209            return Err(ClientError(HmacMissing));
210        };
211
212        let req_pk = context.public_key;
213
214        {
215            let mut lock = self.index_db.lock()?;
216            let db = lock.deref_mut();
217            let usage_cap =
218                Self::get_cap(db, &context.public_key).map_err(|err| internal!("{:?}", err))?;
219
220            let meta = db
221                .metas
222                .get()
223                .get(request.diff.new.id())
224                .ok_or(ClientError(DocumentNotFound))?
225                .clone();
226
227            let mut tree = ServerTree::new(
228                owner,
229                &mut db.owned_files,
230                &mut db.shared_files,
231                &mut db.file_children,
232                &mut db.metas,
233            )?
234            .to_lazy();
235
236            let old_usage = Self::get_usage_helper(&mut tree, db.sizes.get())
237                .map_err(|err| internal!("{:?}", err))?
238                .iter()
239                .map(|f| f.size_bytes)
240                .sum::<u64>();
241            let old_size = db.sizes.get().get(request.diff.id()).unwrap_or(&0);
242            let new_size = request.new_content.value.len() as u64;
243
244            let new_usage = old_usage - old_size + new_size;
245
246            debug!(?old_usage, ?new_usage, ?usage_cap, "usage caps on change doc");
247
248            if new_usage > usage_cap {
249                return Err(ClientError(UsageIsOverDataCap));
250            }
251
252            let meta_owner = meta.owner();
253
254            let direct_access = meta_owner.0 == req_pk;
255
256            if tree.maybe_find(request.diff.new.id()).is_none() {
257                return Err(ClientError(NotPermissioned));
258            }
259
260            let mut share_access = false;
261            if !direct_access {
262                for ancestor in tree
263                    .ancestors(request.diff.id())?
264                    .iter()
265                    .chain(vec![request.diff.new.id()])
266                {
267                    let meta = tree.find(ancestor)?;
268
269                    if meta
270                        .user_access_keys()
271                        .iter()
272                        .any(|access| access.encrypted_for == req_pk)
273                    {
274                        share_access = true;
275                        break;
276                    }
277                }
278            }
279
280            if !direct_access && !share_access {
281                return Err(ClientError(NotPermissioned));
282            }
283
284            let meta = &tree
285                .maybe_find(request.diff.new.id())
286                .ok_or(ClientError(DocumentNotFound))?
287                .file;
288
289            if let Some(old) = &request.diff.old {
290                if meta != old {
291                    return Err(ClientError(OldVersionIncorrect));
292                }
293            }
294
295            if tree.calculate_deleted(request.diff.new.id())? {
296                return Err(ClientError(DocumentDeleted));
297            }
298
299            // Here is where you would check if the person is out of space as a result of the new file.
300            // You could make this a transaction and check whether or not this is an increase in size or
301            // a reduction
302        }
303
304        let new_version = get_time().0 as u64;
305        let new = request.diff.new.clone().add_time(new_version);
306        self.document_service
307            .insert(
308                request.diff.new.id(),
309                request.diff.new.document_hmac().unwrap(),
310                &request.new_content,
311            )
312            .await?;
313        debug!(?id, ?hmac, "Inserted document contents");
314
315        let result = || {
316            let mut lock = self.index_db.lock()?;
317            let db = lock.deref_mut();
318            let tx = db.begin_transaction()?;
319
320            let mut tree = ServerTree::new(
321                owner,
322                &mut db.owned_files,
323                &mut db.shared_files,
324                &mut db.file_children,
325                &mut db.metas,
326            )?
327            .to_lazy();
328            let new_size = request.new_content.value.len() as u64;
329
330            if tree.calculate_deleted(request.diff.new.id())? {
331                return Err(ClientError(DocumentDeleted));
332            }
333
334            let meta = &tree
335                .maybe_find(request.diff.new.id())
336                .ok_or(ClientError(DocumentNotFound))?
337                .file;
338
339            if let Some(old) = &request.diff.old {
340                if meta != old {
341                    return Err(ClientError(OldVersionIncorrect));
342                }
343            }
344
345            db.sizes.insert(*meta.id(), new_size)?;
346            tree.stage(vec![new]).promote()?;
347            db.last_seen.insert(owner, get_time().0 as u64)?;
348
349            tx.drop_safely()?;
350            drop(lock);
351            Ok(())
352        };
353
354        let result = result();
355
356        if result.is_err() {
357            // Cleanup the NEW file created if, for some reason, the tx failed
358            self.document_service
359                .delete(request.diff.new.id(), request.diff.new.document_hmac().unwrap())
360                .await?;
361            debug!(?id, ?hmac, "Cleaned up new document contents after failed metadata update");
362        }
363
364        result?;
365
366        // New
367        if let Some(hmac) = request.diff.old.unwrap().document_hmac() {
368            self.document_service
369                .delete(request.diff.new.id(), hmac)
370                .await?;
371            let old_hmac = base64::encode_config(hmac, base64::URL_SAFE);
372            debug!(
373                ?id,
374                ?old_hmac,
375                "Cleaned up old document contents after successful metadata update"
376            );
377        }
378
379        Ok(())
380    }
381
382    pub async fn get_document(
383        &self, context: RequestContext<GetDocRequest>,
384    ) -> Result<GetDocumentResponse, ServerError<GetDocumentError>> {
385        let request = &context.request;
386        {
387            let mut lock = self.index_db.lock()?;
388            let db = lock.deref_mut();
389            let tx = db.begin_transaction()?;
390
391            let meta_exists = db.metas.get().get(&request.id).is_some();
392
393            let mut tree = ServerTree::new(
394                Owner(context.public_key),
395                &mut db.owned_files,
396                &mut db.shared_files,
397                &mut db.file_children,
398                &mut db.metas,
399            )?
400            .to_lazy();
401
402            let meta = match tree.maybe_find(&request.id) {
403                Some(meta) => Ok(meta),
404                None => Err(if meta_exists {
405                    ClientError(GetDocumentError::NotPermissioned)
406                } else {
407                    ClientError(GetDocumentError::DocumentNotFound)
408                }),
409            }?;
410
411            let hmac = meta
412                .document_hmac()
413                .ok_or(ClientError(GetDocumentError::DocumentNotFound))?;
414
415            if request.hmac != *hmac {
416                return Err(ClientError(GetDocumentError::DocumentNotFound));
417            }
418
419            if tree.calculate_deleted(&request.id)? {
420                return Err(ClientError(GetDocumentError::DocumentNotFound));
421            }
422
423            tx.drop_safely()?;
424        }
425
426        let content = self
427            .document_service
428            .get(&request.id, &request.hmac)
429            .await?;
430        Ok(GetDocumentResponse { content })
431    }
432
433    pub async fn get_file_ids(
434        &self, context: RequestContext<GetFileIdsRequest>,
435    ) -> Result<GetFileIdsResponse, ServerError<GetFileIdsError>> {
436        let owner = Owner(context.public_key);
437        let mut db = self.index_db.lock()?;
438        let db = db.deref_mut();
439
440        Ok(GetFileIdsResponse {
441            ids: ServerTree::new(
442                owner,
443                &mut db.owned_files,
444                &mut db.shared_files,
445                &mut db.file_children,
446                &mut db.metas,
447            )?
448            .owned_ids(),
449        })
450    }
451
452    pub async fn get_updates(
453        &self, context: RequestContext<GetUpdatesRequest>,
454    ) -> Result<GetUpdatesResponse, ServerError<GetUpdatesError>> {
455        let request = &context.request;
456        let owner = Owner(context.public_key);
457
458        let mut db = self.index_db.lock()?;
459        let db = db.deref_mut();
460        let mut tree = ServerTree::new(
461            owner,
462            &mut db.owned_files,
463            &mut db.shared_files,
464            &mut db.file_children,
465            &mut db.metas,
466        )?
467        .to_lazy();
468
469        let mut result_ids = HashSet::new();
470        for id in tree.owned_ids() {
471            let file = tree.find(&id)?;
472            if file.version >= request.since_metadata_version {
473                result_ids.insert(id);
474                if file.owner() != owner
475                    && file
476                        .user_access_keys()
477                        .iter()
478                        .any(|k| !k.deleted && k.encrypted_for == context.public_key)
479                {
480                    result_ids.insert(id);
481                    result_ids.extend(tree.descendants(&id)?);
482                }
483            }
484        }
485
486        Ok(GetUpdatesResponse {
487            as_of_metadata_version: get_time().0 as u64,
488            file_metadata: tree
489                .all_files()?
490                .iter()
491                .filter(|meta| result_ids.contains(meta.id()))
492                .map(|meta| meta.file.clone())
493                .collect(),
494        })
495    }
496
497    pub async fn admin_disappear_file(
498        &self, context: RequestContext<AdminDisappearFileRequest>,
499    ) -> Result<(), ServerError<AdminDisappearFileError>> {
500        let mut docs_to_delete = Vec::new();
501
502        {
503            let mut db = self.index_db.lock()?;
504            let db = db.deref_mut();
505            let tx = db.begin_transaction()?;
506
507            if !Self::is_admin::<AdminDisappearFileError>(
508                db,
509                &context.public_key,
510                &self.config.admin.admins,
511            )? {
512                return Err(ClientError(AdminDisappearFileError::NotPermissioned));
513            }
514
515            let owner = {
516                let meta = db
517                    .metas
518                    .get()
519                    .get(&context.request.id)
520                    .ok_or(ClientError(AdminDisappearFileError::FileNonexistent))?;
521                if meta.is_root() {
522                    return Err(ClientError(AdminDisappearFileError::RootModificationInvalid));
523                }
524                meta.owner()
525            };
526            let mut tree = ServerTree::new(
527                owner,
528                &mut db.owned_files,
529                &mut db.shared_files,
530                &mut db.file_children,
531                &mut db.metas,
532            )?
533            .to_lazy();
534
535            let metas_to_delete = {
536                let mut metas_to_delete = tree.descendants(&context.request.id)?;
537                metas_to_delete.insert(context.request.id);
538                metas_to_delete
539            };
540            for id in metas_to_delete.clone() {
541                if !tree.calculate_deleted(&id)? {
542                    let meta = tree.find(&id)?;
543                    if meta.is_document() && meta.owner() == owner {
544                        if let Some(hmac) = meta.document_hmac() {
545                            docs_to_delete.push((*meta.id(), *hmac));
546                            db.sizes.remove(&id)?;
547                        }
548                    }
549                }
550            }
551
552            for id in metas_to_delete {
553                let meta = db
554                    .metas
555                    .remove(&id)?
556                    .ok_or(ClientError(AdminDisappearFileError::FileNonexistent))?;
557
558                // maintain index: owned_files
559                let owner = meta.owner();
560
561                if !db.owned_files.remove(&owner, &id)? {
562                    error!(
563                        ?id,
564                        ?owner,
565                        "attempted to disappear a file, owner or id not present in owned_files"
566                    );
567                }
568
569                // maintain index: shared_files
570                for user_access_key in meta.user_access_keys() {
571                    let sharee = Owner(user_access_key.encrypted_for);
572                    if !db.shared_files.remove(&sharee, &id)? {
573                        error!(
574                            ?id,
575                            ?sharee,
576                            "attempted to disappear a file, a sharee didn't have it shared"
577                        );
578                    }
579                }
580
581                // maintain index: file_children
582                let parent = *meta.parent();
583                if !db.file_children.remove(meta.parent(), &id)? {
584                    error!(
585                        ?id,
586                        ?parent,
587                        "attempted to disappear a file, the parent didn't have it as a child"
588                    );
589                }
590            }
591
592            let username = db
593                .accounts
594                .get()
595                .get(&Owner(context.public_key))
596                .map(|account| account.username.clone())
597                .unwrap_or_else(|| "~unknown~".to_string());
598            warn!(?username, ?context.request.id, "Disappeared file");
599
600            tx.drop_safely()?;
601        }
602
603        for (id, version) in docs_to_delete {
604            self.document_service.delete(&id, &version).await?;
605        }
606
607        Ok(())
608    }
609
610    pub async fn admin_validate_account(
611        &self, context: RequestContext<AdminValidateAccountRequest>,
612    ) -> Result<AdminValidateAccount, ServerError<AdminValidateAccountError>> {
613        let request = &context.request;
614        let mut db = self.index_db.lock()?;
615        if !Self::is_admin::<AdminValidateAccountError>(
616            &db,
617            &context.public_key,
618            &self.config.admin.admins,
619        )? {
620            return Err(ClientError(AdminValidateAccountError::NotPermissioned));
621        }
622
623        let owner = *db
624            .usernames
625            .get()
626            .get(&request.username)
627            .ok_or(ClientError(AdminValidateAccountError::UserNotFound))?;
628
629        Ok(self.validate_account_helper(&mut db, owner)?)
630    }
631
632    pub fn validate_account_helper(
633        &self, db: &mut ServerDb, owner: Owner,
634    ) -> SharedResult<AdminValidateAccount> {
635        let mut result = AdminValidateAccount::default();
636
637        let mut tree = ServerTree::new(
638            owner,
639            &mut db.owned_files,
640            &mut db.shared_files,
641            &mut db.file_children,
642            &mut db.metas,
643        )?
644        .to_lazy();
645
646        for id in tree.owned_ids() {
647            if !tree.calculate_deleted(&id)? {
648                let file = tree.find(&id)?;
649                if file.is_document() && file.document_hmac().is_some() {
650                    if db.sizes.get().get(&id).is_none() {
651                        result.documents_missing_size.push(id);
652                    }
653
654                    if !self
655                        .document_service
656                        .exists(&id, file.document_hmac().unwrap())
657                    {
658                        result.documents_missing_content.push(id);
659                    }
660                }
661            }
662        }
663
664        let validation_res = tree.stage(None).validate(owner);
665        match validation_res {
666            Ok(_) => {}
667            Err(err) => match err.kind {
668                SharedErrorKind::ValidationFailure(validation) => {
669                    result.tree_validation_failures.push(validation)
670                }
671                _ => {
672                    error!(?owner, ?err, "Unexpected error while validating tree")
673                }
674            },
675        }
676
677        Ok(result)
678    }
679
680    pub async fn admin_validate_server(
681        &self, context: RequestContext<AdminValidateServerRequest>,
682    ) -> Result<AdminValidateServer, ServerError<AdminValidateServerError>> {
683        let mut db = self.index_db.lock()?;
684        let db = db.deref_mut();
685
686        if !Self::is_admin::<AdminValidateServerError>(
687            db,
688            &context.public_key,
689            &self.config.admin.admins,
690        )? {
691            return Err(ClientError(AdminValidateServerError::NotPermissioned));
692        }
693
694        let mut result: AdminValidateServer = Default::default();
695
696        let mut deleted_ids = HashSet::new();
697        for (id, meta) in db.metas.get().clone() {
698            // todo: optimize
699            let mut tree = ServerTree::new(
700                meta.owner(),
701                &mut db.owned_files,
702                &mut db.shared_files,
703                &mut db.file_children,
704                &mut db.metas,
705            )?
706            .to_lazy();
707            if tree.calculate_deleted(&id)? {
708                deleted_ids.insert(id);
709            }
710        }
711
712        // validate accounts
713        for (owner, account) in db.accounts.get().clone() {
714            let validation = self.validate_account_helper(db, owner)?;
715            if !validation.is_empty() {
716                result
717                    .users_with_validation_failures
718                    .insert(account.username, validation);
719            }
720        }
721
722        // validate index: usernames
723        for (username, owner) in db.usernames.get().clone() {
724            if let Some(account) = db.accounts.get().get(&owner) {
725                if username != account.username {
726                    result
727                        .usernames_mapped_to_wrong_accounts
728                        .insert(username, account.username.clone());
729                }
730            } else {
731                result
732                    .usernames_mapped_to_nonexistent_accounts
733                    .insert(username, owner);
734            }
735        }
736        for (_, account) in db.accounts.get().clone() {
737            if db.usernames.get().get(&account.username).is_none() {
738                result
739                    .usernames_unmapped_to_accounts
740                    .insert(account.username.clone());
741            }
742        }
743
744        // validate index: owned_files
745        for (owner, ids) in db.owned_files.get().clone() {
746            for id in ids {
747                if let Some(meta) = db.metas.get().get(&id) {
748                    if meta.owner() != owner {
749                        insert(&mut result.owners_mapped_to_unowned_files, owner, id);
750                    }
751                } else {
752                    insert(&mut result.owners_mapped_to_nonexistent_files, owner, id);
753                }
754            }
755        }
756        for (id, meta) in db.metas.get().clone() {
757            if let Some(ids) = db.owned_files.get().get(&meta.owner()) {
758                if !ids.contains(&id) {
759                    insert(&mut result.owners_unmapped_to_owned_files, meta.owner(), *meta.id());
760                }
761            } else {
762                result.owners_unmapped.insert(meta.owner());
763            }
764        }
765
766        // validate index: shared_files
767        for (sharee, ids) in db.shared_files.get().clone() {
768            for id in ids {
769                if let Some(meta) = db.metas.get().get(&id) {
770                    if !meta.user_access_keys().iter().any(|k| {
771                        !k.deleted && k.encrypted_for == sharee.0 && k.encrypted_by != sharee.0
772                    }) {
773                        insert(&mut result.sharees_mapped_to_unshared_files, sharee, id);
774                    }
775                } else {
776                    insert(&mut result.sharees_mapped_to_nonexistent_files, sharee, id);
777                }
778                if deleted_ids.contains(&id) {
779                    insert(&mut result.sharees_mapped_for_deleted_files, sharee, id);
780                }
781            }
782        }
783        for (id, meta) in db.metas.get().clone() {
784            for k in meta.user_access_keys() {
785                if k.deleted {
786                    continue;
787                }
788                let sharee = Owner(k.encrypted_for);
789                if let Some(ids) = db.shared_files.get().get(&sharee) {
790                    let self_share = k.encrypted_for == k.encrypted_by;
791                    let indexed_share = ids.contains(&id);
792                    if self_share && indexed_share {
793                        insert(&mut result.sharees_mapped_for_owned_files, sharee, id);
794                    } else if !self_share && !indexed_share {
795                        insert(&mut result.sharees_unmapped_to_shared_files, sharee, id);
796                    }
797                } else {
798                    result.sharees_unmapped.insert(meta.owner());
799                }
800            }
801        }
802
803        // validate index: file_children
804        for (parent_id, child_ids) in db.file_children.get().clone() {
805            for child_id in child_ids {
806                if let Some(meta) = db.metas.get().get(&child_id) {
807                    if meta.parent() != &parent_id {
808                        insert(
809                            &mut result.files_mapped_as_parent_to_non_children,
810                            parent_id,
811                            child_id,
812                        );
813                    }
814                } else {
815                    insert(
816                        &mut result.files_mapped_as_parent_to_nonexistent_children,
817                        parent_id,
818                        child_id,
819                    );
820                }
821            }
822        }
823        for (id, meta) in db.metas.get().clone() {
824            if let Some(child_ids) = db.file_children.get().get(meta.parent()) {
825                if meta.is_root() && child_ids.contains(&id) {
826                    result.files_mapped_as_parent_to_self.insert(id);
827                } else if !meta.is_root() && !child_ids.contains(&id) {
828                    insert(&mut result.files_unmapped_as_parent_to_children, *meta.parent(), id);
829                }
830            } else {
831                result.files_unmapped_as_parent.insert(*meta.parent());
832            }
833        }
834
835        // validate index: sizes (todo: validate size values)
836        for (id, _) in db.sizes.get().clone() {
837            if let Some(meta) = db.metas.get().get(&id) {
838                if meta.document_hmac().is_none() {
839                    result.sizes_mapped_for_files_without_hmac.insert(id);
840                }
841            } else {
842                result.sizes_mapped_for_nonexistent_files.insert(id);
843            }
844        }
845        for (id, meta) in db.metas.get().clone() {
846            if !deleted_ids.contains(&id)
847                && meta.document_hmac().is_some()
848                && db.sizes.get().get(&id).is_none()
849            {
850                result.sizes_unmapped_for_files_with_hmac.insert(id);
851            }
852        }
853
854        // validate presence of documents
855        for (id, meta) in db.metas.get().clone() {
856            if let Some(hmac) = meta.document_hmac() {
857                if !deleted_ids.contains(&id) && !self.document_service.exists(&id, hmac) {
858                    result.files_with_hmacs_and_no_contents.insert(id);
859                }
860            }
861        }
862
863        Ok(result)
864    }
865
866    pub async fn admin_file_info(
867        &self, context: RequestContext<AdminFileInfoRequest>,
868    ) -> Result<AdminFileInfoResponse, ServerError<AdminFileInfoError>> {
869        let request = &context.request;
870        let mut db = self.index_db.lock()?;
871        let db = db.deref_mut();
872        if !Self::is_admin::<AdminFileInfoError>(
873            db,
874            &context.public_key,
875            &self.config.admin.admins,
876        )? {
877            return Err(ClientError(AdminFileInfoError::NotPermissioned));
878        }
879
880        let file = db
881            .metas
882            .get()
883            .get(&request.id)
884            .ok_or(ClientError(AdminFileInfoError::FileNonexistent))?
885            .clone();
886
887        let mut tree = ServerTree::new(
888            file.owner(),
889            &mut db.owned_files,
890            &mut db.shared_files,
891            &mut db.file_children,
892            &mut db.metas,
893        )?
894        .to_lazy();
895
896        let ancestors = tree
897            .ancestors(&request.id)?
898            .into_iter()
899            .filter_map(|id| tree.maybe_find(&id))
900            .cloned()
901            .collect();
902        let descendants = tree
903            .descendants(&request.id)?
904            .into_iter()
905            .filter_map(|id| tree.maybe_find(&id))
906            .cloned()
907            .collect();
908
909        Ok(AdminFileInfoResponse { file, ancestors, descendants })
910    }
911
912    pub async fn admin_rebuild_index(
913        &self, context: RequestContext<AdminRebuildIndexRequest>,
914    ) -> Result<(), ServerError<AdminRebuildIndexError>> {
915        let mut db = self.index_db.lock()?;
916
917        match context.request.index {
918            ServerIndex::OwnedFiles => {
919                db.owned_files.clear()?;
920                for owner in db.accounts.get().clone().keys() {
921                    db.owned_files.create_key(*owner)?;
922                }
923                for (id, file) in db.metas.get().clone() {
924                    db.owned_files.insert(file.owner(), id)?;
925                }
926            }
927            ServerIndex::SharedFiles => {
928                db.shared_files.clear()?;
929                for owner in db.accounts.get().clone().keys() {
930                    db.shared_files.create_key(*owner)?;
931                }
932                for (id, file) in db.metas.get().clone() {
933                    for user_access_key in file.user_access_keys() {
934                        if user_access_key.encrypted_for != user_access_key.encrypted_by {
935                            db.shared_files
936                                .insert(Owner(user_access_key.encrypted_for), id)?;
937                        }
938                    }
939                }
940            }
941            ServerIndex::FileChildren => {
942                db.file_children.clear()?;
943                for id in db.metas.get().clone().keys() {
944                    db.file_children.create_key(*id)?;
945                }
946                for (id, file) in db.metas.get().clone() {
947                    db.file_children.insert(*file.parent(), id)?;
948                }
949            }
950        }
951        Ok(())
952    }
953}
954
955fn insert<K: Hash + Eq, V: Hash + Eq>(map: &mut HashMap<K, HashSet<V>>, k: K, v: V) {
956    map.entry(k).or_default().insert(v);
957}