diff --git a/.github/workflows/quick.yml b/.github/workflows/quick.yml index a86d8d9e..778e0843 100644 --- a/.github/workflows/quick.yml +++ b/.github/workflows/quick.yml @@ -23,6 +23,9 @@ jobs: - name: Block retired managed-library code and contracts run: python3 tests/test_v2_no_managed_library.py + - name: Check native directory/file batch kinds + run: python3 tests/test_cf_acl_dirent_kind.py + - name: Run cheap C-source ACL and Branch retry contracts run: python3 -m unittest discover -s tests -p 'test_cf_*.py' diff --git a/common/cf-acl.c b/common/cf-acl.c index 9f4f8215..2e5fb795 100644 --- a/common/cf-acl.c +++ b/common/cf-acl.c @@ -1,6 +1,7 @@ /* CloudFile V2 native permission seam; no library enrollment or class. */ #include "common.h" #include +#include #include #include "log.h" #include "seafile-session.h" @@ -95,16 +96,21 @@ filter_chunk (const char *repo, const char *dir, const char *user, for (GList *p=begin; p && count > 0; p=p->next, --count) { GObject *item=p->data; char *name=NULL; - g_object_get(item,"obj_name",&name,NULL); + int mode=0; + g_object_get(item,"obj_name",&name,"mode",&mode,NULL); if (!name || !*name || strchr(name,'/') || - !strcmp(name,".") || !strcmp(name,"..")) { + !strcmp(name,".") || !strcmp(name,"..") || + (!S_ISDIR(mode) && !S_ISREG(mode))) { g_free(name); goto done; } char *path=g_build_filename(dir,name,NULL); g_free(name); + /* File rights follow the containing directory; a directory entry + * follows its own path. Both use Hub's same Authority rule snapshot. */ + const char *kind=S_ISDIR(mode) ? "dir" : "file"; json_t *check=json_pack("{s:s,s:s,s:s}", - "path",path,"kind","dir","action","read"); + "path",path,"kind",kind,"action","read"); g_free(path); if (!check || json_array_append(checks,check)) { json_decref(check); diff --git a/tests/test_cf_acl_dirent_kind.py b/tests/test_cf_acl_dirent_kind.py new file mode 100644 index 00000000..123b1583 --- /dev/null +++ b/tests/test_cf_acl_dirent_kind.py @@ -0,0 +1,19 @@ +"""Cheap contract: native batch sends file vs directory kind to Hub Authority.""" +from pathlib import Path +import unittest + + +class NativeDirentKindTests(unittest.TestCase): + def test_native_permission_batch_uses_real_dirent_mode(self): + root = Path(__file__).resolve().parents[1] + source = (root / 'common' / 'cf-acl.c').read_text(encoding='utf-8') + chunk = source.split('filter_chunk (', 1)[1].split('\nGList *\ncf_acl_filter_dirents', 1)[0] + self.assertIn('"mode",&mode', chunk) + self.assertIn('S_ISDIR(mode) ? "dir" : "file"', chunk) + self.assertIn('"path",path,"kind",kind,"action","read"', chunk) + self.assertIn('cf_v2_acl_check_json(seaf,payload,body)', chunk) + self.assertIn('json_array_size(permissions)!=size', chunk) + + +if __name__ == '__main__': + unittest.main()