Sqlite: Add option for read-only detection [#509]

pull/620/head
gnosygnu 5 years ago
parent 7f76d8128d
commit 989ccde83a

@ -14,7 +14,8 @@ GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx;
import gplx.core.primitives.*; import gplx.core.ios.*; /*IoItmFil, IoItmDir..*/ import gplx.core.ios.streams.*; import gplx.core.ios.loaders.*;
import gplx.core.primitives.*;
import gplx.core.ios.*; /*IoItmFil, IoItmDir..*/ import gplx.core.ios.streams.*; import gplx.core.ios.loaders.*; import gplx.core.ios.atrs.*;
public class Io_mgr implements Gfo_evt_mgr_owner { // exists primarily to gather all cmds under gplx namespace; otherwise need to use gplx.core.ios whenever copying/deleting file
public Io_mgr() {evt_mgr = new Gfo_evt_mgr(this);}
public Gfo_evt_mgr Evt_mgr() {return evt_mgr;} private final Gfo_evt_mgr evt_mgr;
@ -57,6 +58,7 @@ public class Io_mgr implements Gfo_evt_mgr_owner { // exists primarily to gather
}
public Io_url[] QueryDir_fils(Io_url dir) {return QueryDir_args(dir).ExecAsUrlAry();}
public IoEngine_xrg_queryDir QueryDir_args(Io_url dir) {return IoEngine_xrg_queryDir.new_(dir);}
public Io_itm_atr_req Query_itm_atrs(Io_url url, Io_itm_atr_req req) {return IoEnginePool.Instance.Get_by(url.Info().EngineKey()).Query_itm_atrs(url, req);}
public void DeleteDirSubs(Io_url url) {IoEngine_xrg_deleteDir.new_(url).Exec();}
public IoEngine_xrg_deleteDir DeleteDir_cmd(Io_url url) {return IoEngine_xrg_deleteDir.new_(url);}
public void DeleteDirDeep(Io_url url) {IoEngine_xrg_deleteDir.new_(url).Recur_().Exec();}
@ -149,11 +151,13 @@ public class Io_mgr implements Gfo_evt_mgr_owner { // exists primarily to gather
}
public boolean DownloadFil(String src, Io_url trg) {return IoEngine_xrg_downloadFil.new_(src, trg).Exec();}
public IoEngine_xrg_downloadFil DownloadFil_args(String src, Io_url trg) {return IoEngine_xrg_downloadFil.new_(src, trg);}
public boolean Query_read_only(Io_url url, int read_only_type) {return IoEngineUtl.Query_read_only(IoEnginePool.Instance.Get_by(url.Info().EngineKey()), url, read_only_type);}
public static final Io_mgr Instance = new Io_mgr();
public static final int Len_kb = 1024, Len_mb = 1048576, Len_gb = 1073741824, Len_gb_2 = 2147483647;
public static final long Len_mb_long = Len_mb;
public static final long Len_null = -1;
public static final String Evt__fil_created = "fil_created";
public static final int Read_only__basic__file = 1, Read_only__basic__file_and_dirs = 2, Read_only__perms__file = 3;
}
class Io_mgr_ {
public static int Delete_dir_empty(Io_url url) {

@ -21,14 +21,14 @@ public class Ordered_hash_base extends Hash_adp_base implements Ordered_hash, Gf
@Override protected void Add_base(Object key, Object val) {
super.Add_base(key, val);
ordered.Add(val);
AssertCounts();
AssertCounts("Add_base", key);
}
@Override public void Del(Object key) {
if (!this.Has_base(key)) return;
Object val = this.Fetch_base(key);
this.Del_base(key);
ordered.Del(val);
AssertCounts();
AssertCounts("Del", key);
}
protected Object Get_at_base(int index) {return ordered.Get_at(index);}
protected int IndexOf_base(Object obj) {return ordered.Idx_of(obj);}
@ -50,7 +50,7 @@ public class Ordered_hash_base extends Hash_adp_base implements Ordered_hash, Gf
if (locked) Lock_fail();
super.Add_base(key, val);
ordered.Add_at(i, val);
AssertCounts();
AssertCounts("Add_at", key);
}
public Ordered_hash Add_many_str(String... ary) {
int ary_len = ary.length;
@ -61,8 +61,8 @@ public class Ordered_hash_base extends Hash_adp_base implements Ordered_hash, Gf
}
return this;
}
void AssertCounts() {
if (super.Count() != ordered.Count()) throw Err_.new_wo_type("counts do not match", "hash", super.Count(), "list", ordered.Count());
private void AssertCounts(String proc, Object key) {
if (super.Count() != ordered.Count()) throw Err_.new_wo_type("counts do not match; same key is either added twice, or delete failed", "proc", proc, "key", Object_.Xto_str_strict_or_null_mark(key), "hash", super.Count(), "list", ordered.Count());
}
public void Resize_bounds(int i) {if (locked) Lock_fail(); ordered.Resize_bounds(i);}
public void Lock() {locked = true;} private boolean locked = false;

@ -13,19 +13,24 @@ The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.core.lists.caches; import gplx.*; import gplx.core.*; import gplx.core.lists.*;
package gplx.core.caches; import gplx.*; import gplx.core.*;
public class Lru_cache {
private final Hash_adp map = Hash_adp_.New();
private long max;
private long cur = 0;
private Lru_node head;
private Lru_node tail;
private long evicts;
public void Max_(long max) {
private Lru_node head, tail;
private long cur, min, max, evicts;
public Lru_cache(boolean auto_reg, String key, long min, long max) {
this.key = key;
this.min = min;
this.max = max;
if (auto_reg) Lru_cache_root.Instance.Add(this);
}
public String Key() {return key;} private final String key;
public long Evicts() {return evicts;}
public long Cur() {return cur;}
public void Min_max_(long min, long max) {
this.min = min;
this.max = max;
}
public Object Get_or_null(Object key) {
Lru_node nde = (Lru_node)map.Get_by(key);
if (nde == null) {
@ -46,10 +51,7 @@ public class Lru_cache {
Add_to_tail(nde);
}
else {
while (cur + size > max) {
Del_node_from_this(head);
evicts++;
}
this.Clear_min(size);
nde = new Lru_node(key, val, size);
Add_to_tail(nde);
@ -63,12 +65,19 @@ public class Lru_cache {
Del_node_from_this(nde);
}
}
public void Clear() {
public void Clear_all() {
map.Clear();
head = null;
tail = null;
cur = 0;
}
public void Clear_min(long size) {
long threshold = min >= 0 ? min : max;
while (cur + size > threshold) {
Del_node_from_this(head);
evicts++;
}
}
private void Del_node_from_this(Lru_node nde) {
map.Del(nde.Key());
cur -= nde.Size();
@ -96,6 +105,21 @@ public class Lru_cache {
if (head == null)
head = tail;
}
public void To_str(Bry_bfr bfr, boolean grps_only_or_both) {
bfr.Add_str_a7("g");
bfr.Add_byte_pipe().Add_str_u8(key);
bfr.Add_byte_pipe().Add_long_variable(cur);
bfr.Add_byte_pipe().Add_long_variable(min);
bfr.Add_byte_pipe().Add_long_variable(max);
bfr.Add_byte_nl();
if (grps_only_or_both) {
Lru_node nde = head;
while (nde != null) {
nde.To_str(bfr);
nde = nde.Nxt();
}
}
}
}
class Lru_node {
private final Object key;
@ -114,4 +138,10 @@ class Lru_node {
public long Size() {return size;}
public Lru_node Prv() {return prv;} public void Prv_(Lru_node v) {this.prv = v;}
public Lru_node Nxt() {return nxt;} public void Nxt_(Lru_node v) {this.nxt = v;}
public void To_str(Bry_bfr bfr) {
bfr.Add_str_a7("i");
bfr.Add_byte_pipe().Add_str_u8(Object_.Xto_str_strict_or_null_mark(key));
bfr.Add_byte_pipe().Add_long_variable(size);
bfr.Add_byte_nl();
}
}

@ -0,0 +1,50 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2017 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
You may use XOWA according to either of these licenses as is most appropriate
for your project on a case-by-case basis.
The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.core.caches; import gplx.*; import gplx.core.*;
public class Lru_cache_root {
private final Ordered_hash hash = Ordered_hash_.New();
public Lru_cache Get_by_key(String key) {return (Lru_cache)hash.Get_by(key);}
public void Add(Lru_cache grp) {
hash.Add(grp.Key(), grp);
}
public void Del(String key) {
hash.Del(key);
}
public void Clear_caches_all() {
int len = hash.Len();
for (int i = 0; i < len; i++) {
Lru_cache grp = (Lru_cache)hash.Get_at(i);
grp.Clear_all();
}
}
public void Clear_caches_min() {
int len = hash.Len();
for (int i = 0; i < len; i++) {
Lru_cache grp = (Lru_cache)hash.Get_at(i);
grp.Clear_min(0);
}
}
public String Print_contents(boolean grps_only_or_both) {
Bry_bfr bfr = Bry_bfr_.New();
int len = hash.Len();
for (int i = 0; i < len; i++) {
Lru_cache grp = (Lru_cache)hash.Get_at(i);
grp.To_str(bfr, grps_only_or_both);
}
return bfr.To_str_and_clear();
}
public static final Lru_cache_root Instance = new Lru_cache_root();
}

@ -13,25 +13,21 @@ The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.core.lists.caches; import gplx.*; import gplx.core.*; import gplx.core.lists.*;
package gplx.core.caches; import gplx.*; import gplx.core.*;
import org.junit.*; import gplx.core.tests.*;
import gplx.xowa.wikis.data.tbls.*; import gplx.xowa.wikis.nss.*;
public class Lru_cache_tst {
private final Lru_cache_fxt fxt = new Lru_cache_fxt();
@Test public void Get_one() {
fxt.Init__max(10);
fxt.Exec__set("a", 5);
fxt.Test__get_y("a");
}
@Test public void Pop_one() {
fxt.Init__max(10);
fxt.Exec__set("a", 10);
fxt.Exec__set("b", 10);
fxt.Test__get_n("a");
fxt.Test__get_y("b");
}
@Test public void Add_many() {
fxt.Init__max(10);
fxt.Exec__set("a", 4);
fxt.Exec__set("b", 3);
fxt.Exec__set("c", 2);
@ -39,7 +35,6 @@ public class Lru_cache_tst {
fxt.Test__get_y("a", "b", "c", "d");
}
@Test public void Pop_many() {
fxt.Init__max(10);
fxt.Exec__set("a", 4);
fxt.Exec__set("b", 3);
fxt.Exec__set("c", 2);
@ -49,14 +44,12 @@ public class Lru_cache_tst {
fxt.Test__get_n("a", "b");
}
@Test public void Set_repeatedly() {
fxt.Init__max(10);
fxt.Exec__set("a", "a1", 10);
fxt.Exec__set("a", "a2", 10);
fxt.Exec__set("a", "a3", 10);
fxt.Test__get_val("a", "a3");
}
@Test public void Set_bumps_priority() {
fxt.Init__max(10);
fxt.Exec__set("a", 2);
fxt.Exec__set("b", 3);
fxt.Exec__set("c", 2);
@ -66,7 +59,6 @@ public class Lru_cache_tst {
fxt.Test__get_n("b", "c");
}
@Test public void Del() {
fxt.Init__max(10);
fxt.Exec__set("a", 2);
fxt.Exec__set("b", 2);
fxt.Exec__del("b");
@ -74,7 +66,6 @@ public class Lru_cache_tst {
fxt.Test__get_n("b");
}
@Test public void Clear() {
fxt.Init__max(10);
fxt.Exec__set("a", 2);
fxt.Exec__set("b", 2);
fxt.Exec__clear();
@ -82,10 +73,7 @@ public class Lru_cache_tst {
}
}
class Lru_cache_fxt {
private final Lru_cache cache = new Lru_cache();
public void Init__max(long max) {
cache.Max_(max);
}
private final Lru_cache cache = new Lru_cache(Bool_.N, "test", -1, 10);
public void Exec__set(String key, long size) {
cache.Set(key, key, size);
}
@ -96,7 +84,7 @@ class Lru_cache_fxt {
cache.Del(key);
}
public void Exec__clear() {
cache.Clear();
cache.Clear_all();
}
public void Test__get_y(String... keys) {
for (String key : keys)

@ -15,7 +15,7 @@ Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.core.ios; import gplx.*; import gplx.core.*;
import gplx.core.consoles.*; import gplx.core.criterias.*;
import gplx.core.ios.streams.*;
import gplx.core.ios.streams.*; import gplx.core.ios.atrs.*;
public interface IoEngine {
String Key();
boolean ExistsFil_api(Io_url url);
@ -32,6 +32,7 @@ public interface IoEngine {
void XferFil(IoEngine_xrg_xferFil args);
void RecycleFil(IoEngine_xrg_recycleFil xrg);
boolean Truncate_fil(Io_url url, long size);
Io_itm_atr_req Query_itm_atrs(Io_url url, Io_itm_atr_req req);
boolean ExistsDir(Io_url url);
void CreateDir(Io_url url); // creates all folder levels (EX: C:\a\b\c\ will create C:\a\ and C:\a\b\). will not fail if called on already existing folders.
@ -47,108 +48,3 @@ public interface IoEngine {
boolean DownloadFil(IoEngine_xrg_downloadFil xrg);
Io_stream_rdr DownloadFil_as_rdr(IoEngine_xrg_downloadFil xrg);
}
class IoEngineUtl {
public int BufferLength() {return bufferLength;} public void BufferLength_set(int v) {bufferLength = v;} int bufferLength = 4096; // 0x1000
public void DeleteRecycleGplx(IoEngine engine, IoEngine_xrg_recycleFil xrg) {
Io_url recycleUrl = xrg.RecycleUrl();
if (recycleUrl.Type_fil())
engine.MoveFil(IoEngine_xrg_xferFil.move_(xrg.Url(), recycleUrl).Overwrite_(false).ReadOnlyFails_(true));
else
engine.MoveDirDeep(IoEngine_xrg_xferDir.move_(xrg.Url(), recycleUrl).Overwrite_(false).ReadOnlyFails_(true));
}
public void DeleteDirDeep(IoEngine engine, Io_url dirUrl, IoEngine_xrg_deleteDir args) {
Console_adp usrDlg = args.UsrDlg();
IoItmDir dir = engine.QueryDir(dirUrl); if (!dir.Exists()) return;
for (Object subDirObj : dir.SubDirs()) {
IoItmDir subDir = (IoItmDir)subDirObj;
if (!args.SubDirScanCrt().Matches(subDir)) continue;
if (args.Recur()) DeleteDirDeep(engine, subDir.Url(), args);
}
for (Object subFilObj : dir.SubFils()) {
IoItmFil subFil = (IoItmFil)subFilObj;
if (!args.MatchCrt().Matches(subFil)) continue;
Io_url subFilUrl = subFil.Url();
try {engine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(subFilUrl).ReadOnlyFails_(args.ReadOnlyFails()));}
catch (Exception exc) {usrDlg.Write_fmt_w_nl(Err_.Message_lang(exc));}
}
// all subs deleted; now delete dir
if (!args.MatchCrt().Matches(dir)) return;
try {engine.DeleteDir(dir.Url());}
catch (Exception exc) {usrDlg.Write_fmt_w_nl(Err_.Message_lang(exc));}
}
public void XferDir(IoEngine srcEngine, Io_url src, IoEngine trgEngine, Io_url trg, IoEngine_xrg_xferDir args) {
trgEngine.CreateDir(trg);
IoItmDir srcDir = QueryDirDeep(srcEngine, IoEngine_xrg_queryDir.new_(src).Recur_(false));
for (Object subSrcObj : srcDir.SubDirs()) {
IoItmDir subSrc = (IoItmDir)subSrcObj;
if (!args.SubDirScanCrt().Matches(subSrc)) continue;
if (!args.MatchCrt().Matches(subSrc)) continue;
Io_url subTrg = trg.GenSubDir_nest(subSrc.Url().NameOnly()); //EX: C:\abc\def\ -> C:\123\ + def\
if (args.Recur()) XferDir(srcEngine, subSrc.Url(), trgEngine, subTrg, args);
}
IoItmList srcFils = IoItmList.list_(src.Info().CaseSensitive());
for (Object srcFilObj : srcDir.SubFils()) {
IoItmFil srcFil = (IoItmFil)srcFilObj;
if (args.MatchCrt().Matches(srcFil)) srcFils.Add(srcFil);
}
for (Object srcFilObj : srcFils) {
IoItmFil srcFil = (IoItmFil)srcFilObj;
Io_url srcFilPath = srcFil.Url();
Io_url trgFilPath = trg.GenSubFil(srcFilPath.NameAndExt()); //EX: C:\abc\fil.txt -> C:\123\ + fil.txt
IoEngine_xrg_xferFil xferArgs = args.Type_move() ? IoEngine_xrg_xferFil.move_(srcFilPath, trgFilPath).Overwrite_(args.Overwrite()) : IoEngine_xrg_xferFil.copy_(srcFilPath, trgFilPath).Overwrite_(args.Overwrite());
XferFil(srcEngine, xferArgs);
}
if (args.Type_move()) srcEngine.DeleteDirDeep(IoEngine_xrg_deleteDir.new_(src).Recur_(args.Recur()).ReadOnlyFails_(args.ReadOnlyFails()));// this.DeleteDirDeep(srcEngine, src, IoEngine_xrg_deleteItm.new_(src).Recur_(args.Recur()).ReadOnlyIgnored_(args.ReadOnlyIgnored()));
}
public void XferFil(IoEngine srcEngine, IoEngine_xrg_xferFil args) {
Io_url src = args.Src(), trg = args.Trg();
if (String_.Eq(srcEngine.Key(), trg.Info().EngineKey())) {
if (args.Type_move())
srcEngine.MoveFil(args);
else
srcEngine.CopyFil(args);
}
else {
TransferStream(src, trg);
if (args.Type_move()) srcEngine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(src));
}
}
public IoItmDir QueryDirDeep(IoEngine engine, IoEngine_xrg_queryDir args) {
IoItmDir rv = IoItmDir_.top_(args.Url());
rv.Exists_set(QueryDirDeepCore(rv, args.Url(), engine, args.Recur(), args.SubDirScanCrt(), args.DirCrt(), args.FilCrt(), args.UsrDlg(), args.DirInclude()));
return rv;
}
static boolean QueryDirDeepCore(IoItmDir ownerDir, Io_url url, IoEngine engine, boolean recur, Criteria subDirScanCrt, Criteria dirCrt, Criteria filCrt, Console_adp usrDlg, boolean dirInclude) {
if (usrDlg.Canceled_chk()) return false;
if (usrDlg.Enabled()) usrDlg.Write_tmp(String_.Concat("scan: ", url.Raw()));
IoItmDir scanDir = engine.QueryDir(url);
for (Object subDirObj : scanDir.SubDirs()) {
IoItmDir subDir = (IoItmDir)subDirObj;
if (!subDirScanCrt.Matches(subDir)) continue;
if (dirCrt.Matches(subDir)) {
ownerDir.SubDirs().Add(subDir); // NOTE: always add subDir; do not use dirCrt here, else its subFils will be added to non-existent subDir
}
if (recur)
QueryDirDeepCore(subDir, subDir.Url(), engine, recur, subDirScanCrt, dirCrt, filCrt, usrDlg, dirInclude);
}
for (Object subFilObj : scanDir.SubFils()) {
IoItmFil subFil = (IoItmFil)subFilObj;
if (filCrt.Matches(subFil)) ownerDir.SubFils().Add(subFil);
}
return scanDir.Exists();
}
void TransferStream(Io_url src, Io_url trg) {
IoStream srcStream = null;
IoStream trgStream = null;
try {
srcStream = IoEnginePool.Instance.Get_by(src.Info().EngineKey()).OpenStreamRead(src);
trgStream = IoEngine_xrg_openWrite.new_(trg).Exec();
srcStream.Transfer(trgStream, bufferLength);
}
finally {
if (srcStream != null) srcStream.Rls();
if (trgStream != null) trgStream.Rls();
}
}
public static IoEngineUtl new_() {return new IoEngineUtl();} IoEngineUtl() {}
}

@ -0,0 +1,175 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2017 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
You may use XOWA according to either of these licenses as is most appropriate
for your project on a case-by-case basis.
The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.core.ios; import gplx.*; import gplx.core.*;
import gplx.core.ios.streams.*; import gplx.core.ios.atrs.*;
import gplx.core.consoles.*; import gplx.core.criterias.*; import gplx.core.caches.*;
import gplx.core.envs.*;
public class IoEngineUtl {
public int BufferLength() {return bufferLength;} public void BufferLength_set(int v) {bufferLength = v;} int bufferLength = 4096; // 0x1000
public void DeleteRecycleGplx(IoEngine engine, IoEngine_xrg_recycleFil xrg) {
Io_url recycleUrl = xrg.RecycleUrl();
if (recycleUrl.Type_fil())
engine.MoveFil(IoEngine_xrg_xferFil.move_(xrg.Url(), recycleUrl).Overwrite_(false).ReadOnlyFails_(true));
else
engine.MoveDirDeep(IoEngine_xrg_xferDir.move_(xrg.Url(), recycleUrl).Overwrite_(false).ReadOnlyFails_(true));
}
public void DeleteDirDeep(IoEngine engine, Io_url dirUrl, IoEngine_xrg_deleteDir args) {
Console_adp usrDlg = args.UsrDlg();
IoItmDir dir = engine.QueryDir(dirUrl); if (!dir.Exists()) return;
for (Object subDirObj : dir.SubDirs()) {
IoItmDir subDir = (IoItmDir)subDirObj;
if (!args.SubDirScanCrt().Matches(subDir)) continue;
if (args.Recur()) DeleteDirDeep(engine, subDir.Url(), args);
}
for (Object subFilObj : dir.SubFils()) {
IoItmFil subFil = (IoItmFil)subFilObj;
if (!args.MatchCrt().Matches(subFil)) continue;
Io_url subFilUrl = subFil.Url();
try {engine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(subFilUrl).ReadOnlyFails_(args.ReadOnlyFails()));}
catch (Exception exc) {usrDlg.Write_fmt_w_nl(Err_.Message_lang(exc));}
}
// all subs deleted; now delete dir
if (!args.MatchCrt().Matches(dir)) return;
try {engine.DeleteDir(dir.Url());}
catch (Exception exc) {usrDlg.Write_fmt_w_nl(Err_.Message_lang(exc));}
}
public void XferDir(IoEngine srcEngine, Io_url src, IoEngine trgEngine, Io_url trg, IoEngine_xrg_xferDir args) {
trgEngine.CreateDir(trg);
IoItmDir srcDir = QueryDirDeep(srcEngine, IoEngine_xrg_queryDir.new_(src).Recur_(false));
for (Object subSrcObj : srcDir.SubDirs()) {
IoItmDir subSrc = (IoItmDir)subSrcObj;
if (!args.SubDirScanCrt().Matches(subSrc)) continue;
if (!args.MatchCrt().Matches(subSrc)) continue;
Io_url subTrg = trg.GenSubDir_nest(subSrc.Url().NameOnly()); //EX: C:\abc\def\ -> C:\123\ + def\
if (args.Recur()) XferDir(srcEngine, subSrc.Url(), trgEngine, subTrg, args);
}
IoItmList srcFils = IoItmList.list_(src.Info().CaseSensitive());
for (Object srcFilObj : srcDir.SubFils()) {
IoItmFil srcFil = (IoItmFil)srcFilObj;
if (args.MatchCrt().Matches(srcFil)) srcFils.Add(srcFil);
}
for (Object srcFilObj : srcFils) {
IoItmFil srcFil = (IoItmFil)srcFilObj;
Io_url srcFilPath = srcFil.Url();
Io_url trgFilPath = trg.GenSubFil(srcFilPath.NameAndExt()); //EX: C:\abc\fil.txt -> C:\123\ + fil.txt
IoEngine_xrg_xferFil xferArgs = args.Type_move() ? IoEngine_xrg_xferFil.move_(srcFilPath, trgFilPath).Overwrite_(args.Overwrite()) : IoEngine_xrg_xferFil.copy_(srcFilPath, trgFilPath).Overwrite_(args.Overwrite());
XferFil(srcEngine, xferArgs);
}
if (args.Type_move()) srcEngine.DeleteDirDeep(IoEngine_xrg_deleteDir.new_(src).Recur_(args.Recur()).ReadOnlyFails_(args.ReadOnlyFails()));// this.DeleteDirDeep(srcEngine, src, IoEngine_xrg_deleteItm.new_(src).Recur_(args.Recur()).ReadOnlyIgnored_(args.ReadOnlyIgnored()));
}
public void XferFil(IoEngine srcEngine, IoEngine_xrg_xferFil args) {
Io_url src = args.Src(), trg = args.Trg();
if (String_.Eq(srcEngine.Key(), trg.Info().EngineKey())) {
if (args.Type_move())
srcEngine.MoveFil(args);
else
srcEngine.CopyFil(args);
}
else {
TransferStream(src, trg);
if (args.Type_move()) srcEngine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(src));
}
}
public IoItmDir QueryDirDeep(IoEngine engine, IoEngine_xrg_queryDir args) {
IoItmDir rv = IoItmDir_.top_(args.Url());
rv.Exists_set(QueryDirDeepCore(rv, args.Url(), engine, args.Recur(), args.SubDirScanCrt(), args.DirCrt(), args.FilCrt(), args.UsrDlg(), args.DirInclude()));
return rv;
}
static boolean QueryDirDeepCore(IoItmDir ownerDir, Io_url url, IoEngine engine, boolean recur, Criteria subDirScanCrt, Criteria dirCrt, Criteria filCrt, Console_adp usrDlg, boolean dirInclude) {
if (usrDlg.Canceled_chk()) return false;
if (usrDlg.Enabled()) usrDlg.Write_tmp(String_.Concat("scan: ", url.Raw()));
IoItmDir scanDir = engine.QueryDir(url);
for (Object subDirObj : scanDir.SubDirs()) {
IoItmDir subDir = (IoItmDir)subDirObj;
if (!subDirScanCrt.Matches(subDir)) continue;
if (dirCrt.Matches(subDir)) {
ownerDir.SubDirs().Add(subDir); // NOTE: always add subDir; do not use dirCrt here, else its subFils will be added to non-existent subDir
}
if (recur)
QueryDirDeepCore(subDir, subDir.Url(), engine, recur, subDirScanCrt, dirCrt, filCrt, usrDlg, dirInclude);
}
for (Object subFilObj : scanDir.SubFils()) {
IoItmFil subFil = (IoItmFil)subFilObj;
if (filCrt.Matches(subFil)) ownerDir.SubFils().Add(subFil);
}
return scanDir.Exists();
}
void TransferStream(Io_url src, Io_url trg) {
IoStream srcStream = null;
IoStream trgStream = null;
try {
srcStream = IoEnginePool.Instance.Get_by(src.Info().EngineKey()).OpenStreamRead(src);
trgStream = IoEngine_xrg_openWrite.new_(trg).Exec();
srcStream.Transfer(trgStream, bufferLength);
}
finally {
if (srcStream != null) srcStream.Rls();
if (trgStream != null) trgStream.Rls();
}
}
private static final Lru_cache Dir_cache = new Lru_cache(Bool_.Y, "gplx.ios.dir_cache", 128, 256);
public static boolean Query_read_only(IoEngine engine, Io_url url, int read_only_type) {
switch (read_only_type) {
case Io_mgr.Read_only__basic__file:
return engine.QueryFil(url).Attrib().ReadOnly();
case Io_mgr.Read_only__basic__file_and_dirs:
if (Op_sys.Cur().Tid_is_wnt()) // only examine owner_dirs if wnt
return Query_read_only__file_and_dirs(engine, url);
else
return engine.QueryFil(url).Attrib().ReadOnly();
case Io_mgr.Read_only__perms__file:
return engine.Query_itm_atrs(url, Io_itm_atr_req.New__read_only()).Is_read_only();
default:
throw Err_.new_unhandled_default(read_only_type);
}
}
private static boolean Query_read_only__file_and_dirs(IoEngine engine, Io_url url) {
// if fil
if (url.Type_fil()) {
IoItmFil fil = engine.QueryFil(url);
// if read-only, return true
if (fil.ReadOnly())
return true;
// else, set to owner dir
else
url = url.OwnerDir();
}
// loop until top
while (url != Io_url_.Empty) {
String dir_key = url.Raw();
// check cache first
IoItmDir dir = (IoItmDir)Dir_cache.Get_or_null(dir_key);
if (dir == null) {
// not in cache; query file_system
dir = engine.QueryDir(url);
Dir_cache.Set(dir_key, dir, 1);
}
// if read-only, return true
if (dir.ReadOnly())
return true;
// else, set to owner dir
else
url = url.OwnerDir();
}
return false;
}
public static IoEngineUtl new_() {return new IoEngineUtl();} IoEngineUtl() {}
}

@ -14,7 +14,8 @@ GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.core.ios; import gplx.*; import gplx.core.*;
import gplx.core.ios.streams.*;
import gplx.core.ios.streams.*; import gplx.core.ios.atrs.*;
import gplx.core.caches.*;
public abstract class IoEngine_base implements IoEngine {
public abstract String Key();
public abstract boolean ExistsFil_api(Io_url url);
@ -24,6 +25,7 @@ public abstract class IoEngine_base implements IoEngine {
public abstract void CopyFil(IoEngine_xrg_xferFil args);
public abstract void MoveFil(IoEngine_xrg_xferFil args);
public abstract IoItmFil QueryFil(Io_url url);
public abstract Io_itm_atr_req Query_itm_atrs(Io_url url, Io_itm_atr_req req);
public abstract void UpdateFilAttrib(Io_url url, IoItmAttrib atr); // will fail if file does not exists
public abstract void UpdateFilModifiedTime(Io_url url, DateAdp modified);
public abstract IoStream OpenStreamRead(Io_url url);

@ -14,7 +14,7 @@ GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.core.ios; import gplx.*; import gplx.core.*;
import gplx.core.ios.streams.*;
import gplx.core.ios.streams.*; import gplx.core.ios.atrs.*;
public class IoEngine_memory extends IoEngine_base {
@Override public String Key() {return key;} private String key = IoEngine_.MemKey;
@Override public boolean ExistsFil_api(Io_url url) {return FetchFil(url) != IoItmFil_mem.Null;}
@ -190,6 +190,9 @@ public class IoEngine_memory extends IoEngine_base {
byte[] bry = Bry_.new_u8(FetchFil(Io_url_.mem_fil_(xrg.Src())).Text());
return Io_stream_rdr_.New__mem(bry);
}
@Override public Io_itm_atr_req Query_itm_atrs(Io_url url, Io_itm_atr_req req) {
return req;
}
IoItmHash dirs = IoItmHash.new_();
IoEngineUtl utl = IoEngineUtl.new_();

@ -21,12 +21,8 @@ import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.*;
import java.nio.channels.*;
import java.util.Date;
import javax.print.FlavorException;
import javax.tools.JavaCompiler;
import gplx.core.criterias.*; import gplx.core.bits.*; import gplx.core.envs.*;
import gplx.core.ios.streams.*;
import gplx.core.ios.streams.*; import gplx.core.ios.atrs.*;
import gplx.core.progs.*;
public class IoEngine_system extends IoEngine_base {
@Override public String Key() {return IoEngine_.SysKey;}
@ -42,6 +38,9 @@ public class IoEngine_system extends IoEngine_base {
if (!Fil_Exists(fil)) return;
MarkFileWritable(fil, url, args.ReadOnlyFails(), "DeleteFile");
DeleteFil_lang(fil, url);
}
@Override public Io_itm_atr_req Query_itm_atrs(Io_url url, Io_itm_atr_req req) {
return Io_itm_atr_wkr.New(url).Process(req);
}
@Override public boolean ExistsFil_api(Io_url url) {
File f = new File(url.Xto_api());
@ -158,7 +157,9 @@ public class IoEngine_system extends IoEngine_base {
&& String_.Eq(url.OwnerDir().Raw(), String_.Empty) // folder is drive; EX: "C:"
)
url_api = url_api + "\\"; // add "\\"; else listFiles will return working folder's files, not C:; DATE:2016-04-07
File dirInfo = new File(url_api);
rv.ReadOnly_(!dirInfo.canWrite()); // get read-only flag for directories; ISSUE#:509; DATE:2019-07-11
if (!dirInfo.exists()) {
rv.Exists_set(false);
return rv;

@ -17,7 +17,8 @@ package gplx.core.ios; import gplx.*; import gplx.core.*;
import gplx.core.criterias.*;
public class IoItmDir extends IoItm_base {
public boolean Exists() {return exists;} public void Exists_set(boolean v) {exists = v;} private boolean exists = true;
@Override public int TypeId() {return Type_Dir;} @Override public boolean Type_dir() {return true;} @Override public boolean Type_fil() {return false;} public static final int Type_Dir = 1;
@Override public int TypeId() {return Type_Dir;} @Override public boolean Type_dir() {return true;} @Override public boolean Type_fil() {return false;} public static final int Type_Dir = 1;
public boolean ReadOnly() {return readOnly;} public IoItmDir ReadOnly_(boolean val) {this.readOnly = val; return this;} private boolean readOnly;
@gplx.New public IoItmDir XtnProps_set(String key, Object val) {return (IoItmDir)super.XtnProps_set(key, val);}
public IoItmList SubDirs() {return subDirs;} IoItmList subDirs;
public IoItmList SubFils() {return subFils;} IoItmList subFils;

@ -0,0 +1,35 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2017 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
You may use XOWA according to either of these licenses as is most appropriate
for your project on a case-by-case basis.
The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.core.ios.atrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*;
public class Io_itm_atr_req {
Io_itm_atr_req(boolean ignore_errors, boolean check_read_only) {
this.ignore_errors = ignore_errors;
this.check_read_only = check_read_only;
}
public boolean Check_read_only() {return check_read_only;} private final boolean check_read_only;
public boolean Is_read_only() {return is_read_only;} public void Is_read_only_(boolean v) {this.is_read_only = v;} private boolean is_read_only;
public boolean Ignore_errors() {return ignore_errors;} private final boolean ignore_errors;
public String To_str() {
Keyval[] ary = new Keyval[2];
ary[0] = Keyval_.new_("check_read_only", check_read_only);
ary[1] = Keyval_.new_("is_read_only", is_read_only);
return Keyval_.Ary_to_str(ary);
}
public static Io_itm_atr_req New__read_only() {
return new Io_itm_atr_req(true, true);
}
}

@ -0,0 +1,63 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2017 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
You may use XOWA according to either of these licenses as is most appropriate
for your project on a case-by-case basis.
The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.core.ios.atrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.attribute.AclFileAttributeView;
import java.nio.file.attribute.PosixFileAttributeView;
import java.util.Set;
public abstract class Io_itm_atr_wkr {
private final Path path;
public Io_itm_atr_wkr(Path path) {
this.path = path;
}
public Io_itm_atr_req Process(Io_itm_atr_req req) {
try {
if (req.Check_read_only())
req.Is_read_only_(this.Is_read_only());
}
catch (Exception e) {
Err err = Err_.new_wo_type("query_itm_atrs failed", "url", path.toString(), "atrs", req.To_str(), "e", Err_.Message_gplx_log(e));
if (req.Ignore_errors()) { // https://stackoverflow.com/questions/25163174/get-generic-folder-permissions-like-generic-all-using-javas-aclfileattributev
Gfo_usr_dlg_.Instance.Warn_many("", "", err.To_str__log());
}
else {
throw err;
}
}
return req;
}
public abstract boolean Is_read_only();
public static Io_itm_atr_wkr New(Io_url url) {
File fil = new File(url.Xto_api());
Path path = fil.toPath();
Set<String> supported_views = path.getFileSystem().supportedFileAttributeViews();
if (supported_views.contains("posix")) {
return new Io_itm_atr_wkr__psx(path);
}
// WNT
else if (supported_views.contains("acl")) {
return new Io_itm_atr_wkr__acl(path);
}
else {
String set_string = "";
for (String view : supported_views) {
set_string += view + ";";
}
throw Err_.new_unhandled(set_string);
}
}
}

@ -0,0 +1,93 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2017 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
You may use XOWA according to either of these licenses as is most appropriate
for your project on a case-by-case basis.
The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.core.ios.atrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.AclEntry;
import java.nio.file.attribute.AclEntryPermission;
import java.nio.file.attribute.AclEntryType;
import java.nio.file.attribute.AclFileAttributeView;
import java.util.List;
import java.util.Set;
import gplx.core.primitives.Bool_obj_val;
class Io_itm_atr_wkr__acl extends Io_itm_atr_wkr { private final AclFileAttributeView view;
public Io_itm_atr_wkr__acl(Path path) {
super(path);
this.view = Files.getFileAttributeView(path, AclFileAttributeView.class);
}
@Override public boolean Is_read_only() {
try {
// convert AclEntry to Acl_entry
List<AclEntry> list = view.getAcl();
int len = list.size();
Acl_entry[] ary = new Acl_entry[len];
for (int i = 0; i < len; i++) {
AclEntry under = list.get(i);
ary[i] = new Acl_entry(under.principal().toString(), under.type(), under.permissions());
}
return !Is_permitted(ary, AclEntryPermission.WRITE_DATA);
} catch (Exception e) {
throw Err_.new_exc(e, "", "Is_read_only failed", "e", Err_.Message_lang(e));
}
}
public static boolean Is_permitted(Acl_entry[] ary, AclEntryPermission permission) {
boolean rv = false;
Hash_adp principals = Hash_adp_.New();
for (Acl_entry itm : ary) {
Set<AclEntryPermission> permissions = itm.Permissions();
switch (itm.Type()) {
// If multiple ALLOW entries
// * for same principal, return false if any of them do not have permission
// * for diff principals, return true if any of them does have permissions
case ALLOW: {
// if current principal is forbidden, ignore entry; want to skip lists like Everyone:Forbidden:C:/folder;Everyone:Allowed;C:/
Bool_obj_val forbidden = (Bool_obj_val)principals.Get_by(itm.Principal());
if (forbidden != null) {
continue;
}
if (!permissions.contains(permission) && !rv) {
rv = false;
principals.Add(itm.Principal(), Bool_obj_val.False);
}
else {
rv = true;
}
break;
}
// If any DENY entries, return false on first entry
case DENY: {
if (permissions.contains(permission)) {
return false;
}
break;
}
}
}
return rv;
}
}
class Acl_entry {
public Acl_entry(String principal, AclEntryType type, Set<AclEntryPermission> permissions) {
this.principal = principal;
this.type = type;
this.permissions = permissions;
}
public String Principal() {return principal;} private final String principal;
public AclEntryType Type() {return type;} private final AclEntryType type;
public Set<AclEntryPermission> Permissions() {return permissions;} private final Set<AclEntryPermission> permissions;
}
//#}

@ -0,0 +1,80 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2017 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
You may use XOWA according to either of these licenses as is most appropriate
for your project on a case-by-case basis.
The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.core.ios.atrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*;
import org.junit.*; import gplx.core.tests.*;
import java.nio.file.attribute.AclEntryPermission;
import java.nio.file.attribute.AclEntryType;
import java.util.HashSet;
import java.util.Set;
public class Io_itm_atr_wkr__acl__tst {
private final Io_itm_attrib_wkr__acl__fxt fxt = new Io_itm_attrib_wkr__acl__fxt();
@Test public void Perm_exists() {
fxt.Test__Is_permitted
( Bool_.Y, AclEntryPermission.WRITE_DATA
, fxt.Make__acl("Everyone", AclEntryType.ALLOW, AclEntryPermission.WRITE_DATA)
);
}
@Test public void Perm_missing() {
fxt.Test__Is_permitted
( Bool_.N, AclEntryPermission.WRITE_DATA
, fxt.Make__acl("Everyone", AclEntryType.ALLOW, AclEntryPermission.READ_DATA)
);
}
@Test public void Deny_over_allow() {
fxt.Test__Is_permitted
( Bool_.N, AclEntryPermission.WRITE_DATA
, fxt.Make__acl("Everyone", AclEntryType.ALLOW, AclEntryPermission.WRITE_DATA)
, fxt.Make__acl("Everyone", AclEntryType.DENY , AclEntryPermission.WRITE_DATA)
);
}
@Test public void Same_principal__perm_missing_over_perm_exists() {
/*
EX: SD card wherein acl_list has 2 entries
* Entry[0] | //MACHINE/SHARE | Everyone:READ_DATA/READ_NAMED_ATTRS/EXECUTE/READ_ATTRIBUTES/READ_ACL/SYNCHRONIZE:ALLOW
* Entry[1] | DRIVE_NAME:/ | Everyone:READ_DATA/WRITE_DATA/APPEND_DATA/READ_NAMED_ATTRS/WRITE_NAMED_ATTRS/EXECUTE/DELETE_CHILD/READ_ATTRIBUTES/WRITE_ATTRIBUTES/DELETE/READ_ACL/WRITE_ACL/WRITE_OWNER/SYNCHRONIZE:ALLOW
*/
fxt.Test__Is_permitted
( Bool_.N, AclEntryPermission.WRITE_DATA
, fxt.Make__acl("Everyone", AclEntryType.ALLOW, AclEntryPermission.READ_DATA)
, fxt.Make__acl("Everyone", AclEntryType.ALLOW, AclEntryPermission.READ_DATA, AclEntryPermission.WRITE_DATA)
);
}
@Test public void Diff_principals__perm_exists_over_perm_missing() {
/*
EX: C drive wherein acl_list has 2 entries
* Entry[0] | C:/ | Administrators:READ_DATA/WRITE_DATA
* Entry[1] | C:/ | Everyone:READ_DATA
*/
fxt.Test__Is_permitted
( Bool_.Y, AclEntryPermission.WRITE_DATA
, fxt.Make__acl("Administrators", AclEntryType.ALLOW, AclEntryPermission.WRITE_DATA)
, fxt.Make__acl("Users" , AclEntryType.ALLOW, AclEntryPermission.READ_DATA)
);
}
}
class Io_itm_attrib_wkr__acl__fxt {
public void Test__Is_permitted(boolean expd, AclEntryPermission permission, Acl_entry... entries) {
boolean actl = Io_itm_atr_wkr__acl.Is_permitted(entries, permission);
Gftest.Eq__bool(expd, actl);
}
public Acl_entry Make__acl(String principal, AclEntryType type, AclEntryPermission... perms) {
Set<AclEntryPermission> perm_set = new HashSet<AclEntryPermission>();
for (AclEntryPermission perm : perms)
perm_set.add(perm);
return new Acl_entry(principal, type, perm_set);
}
}

@ -0,0 +1,52 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2017 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
You may use XOWA according to either of these licenses as is most appropriate
for your project on a case-by-case basis.
The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.core.ios.atrs; import gplx.*; import gplx.core.*; import gplx.core.ios.*;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.util.Set;
class Io_itm_atr_wkr__psx extends Io_itm_atr_wkr { private final PosixFileAttributeView view;
public Io_itm_atr_wkr__psx(Path path) {
super(path);
this.view = Files.getFileAttributeView(path, PosixFileAttributeView.class);
}
@Override public boolean Is_read_only() {
try {
// ASSUME:a file is read-only if it is read-only; Note that the directory may need write-access, but not handling it now; REF:https://superuser.com/a/114611
Set<PosixFilePermission> perms = view.readAttributes().permissions();
int perm_flag = Psx__permissions_to_int(perms);
return perm_flag == 0444;
} catch (Exception e) {
throw Err_.new_exc(e, "", "Is_read_only failed", "e", Err_.Message_lang(e));
}
}
public static int Psx__permissions_to_int(Set<PosixFilePermission> psx_perms) {
int rv = 0;
rv |= ((psx_perms.contains(PosixFilePermission.OWNER_READ)) ? 1 << 8 : 0);
rv |= ((psx_perms.contains(PosixFilePermission.OWNER_WRITE)) ? 1 << 7 : 0);
rv |= ((psx_perms.contains(PosixFilePermission.OWNER_EXECUTE)) ? 1 << 6 : 0);
rv |= ((psx_perms.contains(PosixFilePermission.GROUP_READ)) ? 1 << 5 : 0);
rv |= ((psx_perms.contains(PosixFilePermission.GROUP_WRITE)) ? 1 << 4 : 0);
rv |= ((psx_perms.contains(PosixFilePermission.GROUP_EXECUTE)) ? 1 << 3 : 0);
rv |= ((psx_perms.contains(PosixFilePermission.OTHERS_READ)) ? 1 << 2 : 0);
rv |= ((psx_perms.contains(PosixFilePermission.OTHERS_WRITE)) ? 1 << 1 : 0);
rv |= ((psx_perms.contains(PosixFilePermission.OTHERS_EXECUTE)) ? 1 : 0);
return rv;
}
}

@ -24,7 +24,7 @@ public class IoStream_mock implements IoStream {
if (bytes_read > read_limit) bytes_read = read_limit; // stream may limit maximum read; EX: bfr_len of 16k but only 2k will be filled
int bytes_left = data_bry_len - data_bry_pos;
if (bytes_read > bytes_left) bytes_read = bytes_left; // not enough bytes left in data_bry; bytes_read = whatever is left
Bry_.Copy_by_pos(data_bry, data_bry_pos, data_bry_pos + bytes_read, bfr, bfr_bgn);
Bry_.Copy_to(data_bry, data_bry_pos, data_bry_pos + bytes_read, bfr, bfr_bgn);
data_bry_pos += bytes_read;
return bytes_read;
}

@ -16,8 +16,11 @@ Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
package gplx.dbs.engines.sqlite; import gplx.*; import gplx.dbs.*; import gplx.dbs.engines.*;
import java.sql.*;
import gplx.core.stores.*; import gplx.dbs.engines.*; import gplx.dbs.engines.sqlite.*; import gplx.dbs.metas.*; import gplx.dbs.sqls.*;
import gplx.dbs.qrys.*;
import gplx.dbs.qrys.*;
import gplx.core.consoles.Console_adp_;
import gplx.core.consoles.Console_adp__sys;
import gplx.core.ios.IoItmFil;
import org.sqlite.SQLiteConnection;
public class Sqlite_engine extends Db_engine_sql_base {
private final Sqlite_txn_mgr txn_mgr; private final Sqlite_schema_mgr schema_mgr;
@ -68,9 +71,14 @@ public class Sqlite_engine extends Db_engine_sql_base {
// set open_mode flag if conn is read-only; needed else all SELECT queries will be very slow; DATE:2016-09-03
IoItmFil sqlite_fs_itm = Io_mgr.Instance.QueryFil(sqlite_fs_url);
Keyval[] props = sqlite_fs_itm.Exists() && sqlite_fs_itm.ReadOnly() // NOTE: must check if it exists; else missing-file will be marked as readonly connection, and missing-file will sometimes be dynamically created as read-write; DATE:2016-09-04
boolean read_only = sqlite_fs_itm.Exists() // NOTE: must check if it exists; else missing-file will be marked as readonly connection, and missing-file will sometimes be dynamically created as read-write; DATE:2016-09-04
&& Io_mgr.Instance.Query_read_only(sqlite_fs_url.OwnerDir(), Sqlite_engine_.Read_only_detection);
Keyval[] props = read_only
? Keyval_.Ary(Keyval_.new_("open_mode", "1"))
: Keyval_.Ary_empty;
if (read_only) {
Gfo_usr_dlg_.Instance.Note_many("", "", "Sqlite db opened as read-only: url=~{0}", sqlite_fs_url.Xto_api());
}
// open connection
Connection rv = Conn__new_by_url_and_props(sqlite_db_url, props);

@ -74,4 +74,5 @@ public class Sqlite_engine_ {
public static String X_date_to_str(DateAdp v) {return v == Date_null ? "" : v.XtoStr_fmt_iso_8561();}
public static final DateAdp Date_null = null;
public static final byte Wildcard_byte = Byte_ascii.Hash;
public static int Read_only_detection = Io_mgr.Read_only__basic__file;
}

@ -20,11 +20,11 @@ public class Gfo_cache_mgr_base {
public int Compress_to() {return compress_to;} public void Compress_to_(int v) {compress_to = v;} private int compress_to = 8;
protected Object Base_get_or_null(byte[] key) {
Object rv_obj = hash.Get_by(key);
return rv_obj == null ? null : ((Gfo_cache_itm)rv_obj).Val();
return rv_obj == null ? null : ((Gfo_cache_itm_bry)rv_obj).Val();
}
protected void Base_add(byte[] key, Object val) {
if (hash.Count() >= compress_max) Compress();
Gfo_cache_itm itm = new Gfo_cache_itm(key, val);
Gfo_cache_itm_bry itm = new Gfo_cache_itm_bry(key, val);
hash.Add(key, itm);
}
protected void Base_del(byte[] key) {
@ -35,11 +35,11 @@ public class Gfo_cache_mgr_base {
int del_len = hash.Count() - compress_to;
List_adp del_list = List_adp_.New();
for (int i = 0; i < del_len; i++) {
Gfo_cache_itm itm = (Gfo_cache_itm)hash.Get_at(i);
Gfo_cache_itm_bry itm = (Gfo_cache_itm_bry)hash.Get_at(i);
del_list.Add(itm);
}
for (int i = 0; i < del_len; i++) {
Gfo_cache_itm itm = (Gfo_cache_itm)del_list.Get_at(i);
Gfo_cache_itm_bry itm = (Gfo_cache_itm_bry)del_list.Get_at(i);
hash.Del(itm.Key());
}
}

@ -20,17 +20,17 @@ public class Gfo_cache_mgr_bry extends Gfo_cache_mgr_base {
public void Add(byte[] key, Object val) {Base_add(key, val);}
public void Del(byte[] key) {Base_del(key);}
}
class Gfo_cache_itm {
public Gfo_cache_itm(Object key, Object val) {this.key = key; this.val = val; this.Touched_update();}
class Gfo_cache_itm_bry {
public Gfo_cache_itm_bry(Object key, Object val) {this.key = key; this.val = val; this.Touched_update();}
public Object Key() {return key;} private Object key;
public Object Val() {return val;} private Object val;
public long Touched() {return touched;} private long touched;
public Gfo_cache_itm Touched_update() {touched = System_.Ticks(); return this;}
public Gfo_cache_itm_bry Touched_update() {touched = System_.Ticks(); return this;}
}
class Gfo_cache_itm_comparer implements gplx.core.lists.ComparerAble {
public int compare(Object lhsObj, Object rhsObj) {
Gfo_cache_itm lhs = (Gfo_cache_itm)lhsObj;
Gfo_cache_itm rhs = (Gfo_cache_itm)rhsObj;
Gfo_cache_itm_bry lhs = (Gfo_cache_itm_bry)lhsObj;
Gfo_cache_itm_bry rhs = (Gfo_cache_itm_bry)rhsObj;
return Long_.Compare(lhs.Touched(), rhs.Touched());
}
public static final Gfo_cache_itm_comparer Touched_asc = new Gfo_cache_itm_comparer(); // TS.static

@ -149,6 +149,7 @@ public class Xoae_app implements Xoa_app, Gfo_invk {
stage = Xoa_stage_.Tid_init;
user.Init_by_app(this);
cfg.Init_by_app(this);
Ctor_dbs();
user.User_db_mgr().Cache_mgr().Init_by_app(this);
misc_mgr.Init_by_app(this);
user.History_mgr().Init_by_app(this);
@ -208,6 +209,18 @@ public class Xoae_app implements Xoa_app, Gfo_invk {
msg_log.Clear();
wiki_mgr.Free_mem(clear_ctx);
}
private void Ctor_dbs() {
if (gplx.core.envs.Env_.Mode_testing()) return;
String read_only_detection_str = cfg.Get_str_app_or("xowa.app.dbs.sqlite.read_only_detection", "basic_file");
int read_only_detection = Io_mgr.Read_only__basic__file;
if (String_.Eq(read_only_detection_str, "basic_file"))
read_only_detection = Io_mgr.Read_only__basic__file;
else if (String_.Eq(read_only_detection_str, "basic_file_and_dirs"))
read_only_detection = Io_mgr.Read_only__basic__file_and_dirs;
else if (String_.Eq(read_only_detection_str, "perms_file"))
read_only_detection = Io_mgr.Read_only__perms__file;
gplx.dbs.engines.sqlite.Sqlite_engine_.Read_only_detection = read_only_detection;
}
public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {
if (ctx.Match(k, Invk_gui)) return gui_mgr;
else if (ctx.Match(k, Invk_api)) return api_root;

@ -27,6 +27,7 @@ public class Xocfg_type_mgr {
this.Lists__add("list:xowa.addon.http_server.file_retrieve_mode", Keyval_.new_("wait"), Keyval_.new_("skip"), Keyval_.new_("async_server", "async server"));
this.Lists__add("list:xowa.addon.scribunto.engine", "luaj", "lua");
this.Lists__add("list:xowa.addon.math.renderer", Keyval_.new_("mathjax","MathJax"), Keyval_.new_("latex", "LaTeX"));
this.Lists__add("list:xowa.app.dbs.sqlite.read_only_detection", Keyval_.new_("basic_file", "Basic - File only"), Keyval_.new_("basic_file_and_dirs", "Basic - File and parent dirs"), Keyval_.new_("perms_file", "Permissions - File"));
}
public void Lists__add(String key, String... vals) {
int len = vals.length;

@ -33,7 +33,7 @@ public class Xomp_parse_mgr {
Xomp_page_pool page_pool = new Xomp_page_pool(page_pool_loader, cfg.Num_pages_per_wkr());
// cache: preload tmpls and imglinks
Xow_page_cache page_cache = Xomp_tmpl_cache_bldr.New(wiki, cfg.Load_all_templates(), cfg.Page_cache_max());
Xow_page_cache page_cache = Xomp_tmpl_cache_bldr.New(wiki, cfg.Load_all_templates(), cfg.Page_cache_min(), cfg.Page_cache_max());
wiki.App().User().User_db_mgr().Cache_mgr().Enabled_n_(); // disable db lookups of user cache
Xomp_prog_mgr prog_mgr = new Xomp_prog_mgr();
@ -70,6 +70,7 @@ public class Xomp_parse_mgr {
for (int i = 0; i < wkr_len; ++i) {
// make wiki
Xowe_wiki wkr_wiki = Xow_wiki_utl_.Clone_wiki(wiki, wiki.Fsys_mgr().Root_dir());
Lru_cache_root.Instance.Del(wkr_wiki.Cache_mgr().Page_cache().Cache_key());
wkr_wiki.Cache_mgr().Page_cache_(page_cache).Commons_cache_(commons_cache).Ifexist_cache_(ifexist_cache);

@ -42,7 +42,8 @@ public class Xomp_parse_mgr_cfg implements Gfo_invk {
public long Wbase_cache_mru_size() {return wbase_cache_mru_size;} private long wbase_cache_mru_size = 100;
public long Wbase_cache_mru_weight() {return wbase_cache_mru_weight;} private long wbase_cache_mru_weight = 10;
public long Wbase_cache_mru_compress_size() {return wbase_cache_mru_compress_size;} private long wbase_cache_mru_compress_size = 70;
public long Page_cache_max() {return page_cache_max;} private long page_cache_max = 2 * (long)Io_mgr.Len_gb;
public long Page_cache_min() {return page_cache_min;} private long page_cache_min = 1500 * Io_mgr.Len_mb_long;
public long Page_cache_max() {return page_cache_max;} private long page_cache_max = 2000 * Io_mgr.Len_mb_long;
public void Init(Xowe_wiki wiki) {
if (num_wkrs == -1) num_wkrs = gplx.core.envs.Runtime_.Cpu_count();
if (num_pages_in_pool == -1) num_pages_in_pool = num_wkrs * 1000;
@ -77,6 +78,7 @@ public class Xomp_parse_mgr_cfg implements Gfo_invk {
else if (ctx.Match(k, "wbase_cache_mru_size_")) wbase_cache_mru_size = m.ReadLong("v");
else if (ctx.Match(k, "wbase_cache_mru_weight_")) wbase_cache_mru_weight = m.ReadLong("v");
else if (ctx.Match(k, "wbase_cache_mru_compress_size_")) wbase_cache_mru_compress_size = m.ReadLong("v");
else if (ctx.Match(k, "page_cache_min_")) page_cache_min = gplx.core.ios.Io_size_.parse_or(m.ReadStr("v"), page_cache_min);
else if (ctx.Match(k, "page_cache_max_")) page_cache_max = gplx.core.ios.Io_size_.parse_or(m.ReadStr("v"), page_cache_max);
else return Gfo_invk_.Rv_unhandled;
return this;

@ -17,9 +17,9 @@ package gplx.xowa.addons.bldrs.mass_parses.parses.utls; import gplx.*; import gp
import gplx.dbs.*;
import gplx.xowa.wikis.caches.*;
public class Xomp_tmpl_cache_bldr {
public static Xow_page_cache New(Xowe_wiki wiki, boolean fill_all, long page_cache_max) {
public static Xow_page_cache New(Xowe_wiki wiki, boolean fill_all, long page_cache_min, long page_cache_max) {
Xow_page_cache rv = new Xow_page_cache(wiki);
rv.Max_(page_cache_max);
rv.Min_max_(page_cache_min, page_cache_max);
if (fill_all) Fill_all(rv, wiki);
return rv;
}

@ -14,19 +14,21 @@ GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.xowa.wikis.caches; import gplx.*; import gplx.xowa.*; import gplx.xowa.wikis.*;
import gplx.core.lists.caches.*;
import gplx.core.caches.*;
public class Xow_page_cache {
private final Object thread_lock = new Object(); // NOTE: thread-safety needed for xomp since one page-cache is shared across all wkrs
private final Xowe_wiki wiki;
private final Lru_cache cache = new Lru_cache();
private final Lru_cache cache;
private long cache_tries = 0;
private long cache_misses = 0;
public Xow_page_cache(Xowe_wiki wiki) {
this.wiki = wiki;
cache.Max_(16 * Io_mgr.Len_mb);
this.cache_key = "xowa.app.page_cache.'" + wiki.Domain_str() + "'." + wiki.hashCode();
this.cache = new Lru_cache(Bool_.Y, cache_key, 8 * Io_mgr.Len_mb, 16 * Io_mgr.Len_mb);
}
public String Cache_key() {return cache_key;} private final String cache_key;
public void Load_wkr_(Xow_page_cache_wkr v) {this.load_wkr = v;} private Xow_page_cache_wkr load_wkr;
public void Max_(long v) {cache.Max_(v);}
public void Min_max_(long min, long max) {cache.Min_max_(min, max);}
public void Add_itm(String ttl_full_db, Xow_page_cache_itm itm) {
synchronized (thread_lock) {
@ -61,7 +63,7 @@ public class Xow_page_cache {
public void Free_mem(boolean clear_permanent_itms) {
synchronized (thread_lock) { // LOCK:app-level; DATE:2016-07-06
if (clear_permanent_itms) {
cache.Clear();
cache.Clear_all();
}
}
}

Loading…
Cancel
Save