mirror of
				https://github.com/9001/copyparty.git
				synced 2025-10-31 04:32:20 -06:00 
			
		
		
		
	new hook: granular ramdisk detection
This commit is contained in:
		
							parent
							
								
									aace711eb9
								
							
						
					
					
						commit
						efd19af7ca
					
				|  | @ -28,6 +28,9 @@ these are `--xiu` hooks; unlike `xbu` and `xau` (which get executed on every sin | |||
| * [reject-extension.py](reject-extension.py) rejects uploads if they match a list of file extensions | ||||
| * [reloc-by-ext.py](reloc-by-ext.py) redirects an upload to another destination based on the file extension | ||||
|   * good example of the `reloc` [hook effect](https://github.com/9001/copyparty/blob/hovudstraum/docs/devnotes.md#hook-effects) | ||||
| * [reject-and-explain.py](reject-and-explain.py) shows a custom error-message when it rejects an upload | ||||
| * [reject-ramdisk.py](reject-ramdisk.py) rejects the upload if the destination is a ramdisk | ||||
|   * this hook uses the `I` flag which makes it 140x faster, but if the plugin has a bug it may crash copyparty | ||||
| 
 | ||||
| 
 | ||||
| # on message | ||||
|  | @ -35,3 +38,7 @@ these are `--xiu` hooks; unlike `xbu` and `xau` (which get executed on every sin | |||
| * [qbittorrent-magnet.py](qbittorrent-magnet.py) starts downloading a torrent if you post a magnet url | ||||
| * [usb-eject.py](usb-eject.py) adds web-UI buttons to safe-remove usb flashdrives shared through copyparty | ||||
| * [msg-log.py](msg-log.py) is a guestbook; logs messages to a doc in the same folder | ||||
| 
 | ||||
| 
 | ||||
| # general concept demos | ||||
| * [import-me.py](import-me.py) shows how the `I` flag makes the hook 140x faster (but you need to be Very Careful when writing the plugin) | ||||
|  |  | |||
|  | @ -38,7 +38,7 @@ IMPORTANT NOTE: | |||
| """ | ||||
| 
 | ||||
| 
 | ||||
| def main(ka: dict[str, Any]) -> dict[str, str]: | ||||
| def main(ka: dict[str, Any]) -> dict[str, Any]: | ||||
|     # "ka" is a dictionary with info from copyparty... | ||||
| 
 | ||||
|     # but because we are running inside copyparty, we don't need such courtesies; | ||||
|  | @ -47,7 +47,8 @@ def main(ka: dict[str, Any]) -> dict[str, str]: | |||
|     cf = inspect.currentframe().f_back.f_back.f_back | ||||
|     t = "hello from hook; I am able to peek into copyparty's memory like so:\n  function name: %s\n  variables:\n    %s\n" | ||||
|     t2 = "\n    ".join([("%r: %r" % (k, v))[:99] for k, v in cf.f_locals.items()][:9]) | ||||
|     print(t % (cf.f_code, t2)) | ||||
|     logger = ka["log"] | ||||
|     logger(t % (cf.f_code, t2)) | ||||
| 
 | ||||
|     # must return a dictionary with: | ||||
|     #  "rc": the retcode; 0 is ok | ||||
|  |  | |||
							
								
								
									
										72
									
								
								bin/hooks/reject-ramdisk.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								bin/hooks/reject-ramdisk.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,72 @@ | |||
| #!/usr/bin/env python3 | ||||
| 
 | ||||
| import os | ||||
| import threading | ||||
| from argparse import Namespace | ||||
| 
 | ||||
| from jinja2.nodes import Name | ||||
| from copyparty.fsutil import Fstab | ||||
| from typing import Any, Optional | ||||
| 
 | ||||
| 
 | ||||
| _ = r""" | ||||
| reject an upload if the target folder is on a ramdisk; useful when you | ||||
| have a volume where some folders inside are ramdisks but others aren't | ||||
| 
 | ||||
| example usage as global config: | ||||
|     --xbu I,bin/hooks/reject-ramdisk.py | ||||
| 
 | ||||
| example usage as a volflag (per-volume config): | ||||
|     -v srv/inc:inc:r:rw,ed:c,xbu=I,bin/hooks/reject-ramdisk.py | ||||
|                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||||
| 
 | ||||
|     (share filesystem-path srv/inc as volume /inc, | ||||
|      readable by everyone, read-write for user 'ed', | ||||
|      running this plugin on all uploads with the params listed below) | ||||
| 
 | ||||
| example usage as a volflag in a copyparty config file: | ||||
|     [/inc] | ||||
|       srv/inc | ||||
|       accs: | ||||
|         r: * | ||||
|         rw: ed | ||||
|       flags: | ||||
|         xbu: I,bin/hooks/reject-ramdisk.py | ||||
| 
 | ||||
| parameters explained, | ||||
|     I = import; do not fork / subprocess | ||||
| 
 | ||||
| IMPORTANT NOTE: | ||||
|     because this hook is imported inside copyparty, you need to | ||||
|     be EXCEPTIONALLY CAREFUL to avoid side-effects, for example | ||||
|     DO NOT os.chdir() or anything like that, and also make sure | ||||
|     that the name of this file is unique (cannot be the same as | ||||
|     an existing python module/library) | ||||
| """ | ||||
| 
 | ||||
| 
 | ||||
| mutex = threading.Lock() | ||||
| fstab: Optional[Fstab] = None | ||||
| 
 | ||||
| 
 | ||||
| def main(ka: dict[str, Any]) -> dict[str, Any]: | ||||
|     global fstab | ||||
|     with mutex: | ||||
|         log = ka["log"]  # this is a copyparty NamedLogger function | ||||
|         if not fstab: | ||||
|             log("<HOOK:RAMDISK> creating fstab", 6) | ||||
|             args = Namespace() | ||||
|             args.mtab_age = 1  # cache the filesystem info for 1 sec | ||||
|             fstab = Fstab(log, args, False) | ||||
| 
 | ||||
|         ap = ka["ap"]  # abspath the upload is going to | ||||
|         fs, mp = fstab.get(ap)  # figure out what the filesystem is | ||||
|         ramdisk = fs in ("tmspfs", "overlay")  # looks like a ramdisk? | ||||
| 
 | ||||
|         # log("<HOOK:RAMDISK> fs=%r" % (fs,)) | ||||
| 
 | ||||
|         if ramdisk: | ||||
|             t = "Upload REJECTED because destination is a ramdisk" | ||||
|             return {"rc": 1, "rejectmsg": t} | ||||
| 
 | ||||
|         return {"rc": 0} | ||||
|  | @ -3629,7 +3629,7 @@ def retchk( | |||
| 
 | ||||
| def _parsehook( | ||||
|     log: Optional["NamedLogger"], cmd: str | ||||
| ) -> tuple[str, bool, bool, bool, float, dict[str, Any], list[str]]: | ||||
| ) -> tuple[str, bool, bool, bool, bool, float, dict[str, Any], list[str]]: | ||||
|     areq = "" | ||||
|     chk = False | ||||
|     fork = False | ||||
|  | @ -3906,6 +3906,7 @@ def _runhook( | |||
|             "txt": txt, | ||||
|         } | ||||
|         if imp: | ||||
|             ja["log"] = log | ||||
|             mod = loadpy(acmd[0], False) | ||||
|             return mod.main(ja) | ||||
|         arg = json.dumps(ja) | ||||
|  | @ -4003,7 +4004,7 @@ def runhook( | |||
|                 else: | ||||
|                     ret[k] = v | ||||
|         except Exception as ex: | ||||
|             (log or print)("hook: {}".format(ex)) | ||||
|             (log or print)("hook: %r, %s" % (ex, ex)) | ||||
|             if ",c," in "," + cmd: | ||||
|                 return {} | ||||
|             break | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ | |||
|     * [general](#general) | ||||
| * [event hooks](#event-hooks) - on writing your own [hooks](../README.md#event-hooks) | ||||
|     * [hook effects](#hook-effects) - hooks can cause intentional side-effects | ||||
|     * [hook import](#hook-import) - the `I` flag runs the hook inside copyparty | ||||
| * [assumptions](#assumptions) | ||||
|     * [mdns](#mdns) | ||||
| * [sfx repack](#sfx-repack) - reduce the size of an sfx by removing features | ||||
|  | @ -310,7 +311,7 @@ a subset of effect types are available for a subset of hook types, | |||
| to trigger indexing of files `/foo/1.txt` and `/foo/bar/2.txt`, a hook can `print(json.dumps({"idx":{"vp":["/foo/1.txt","/foo/bar/2.txt"]}}))` (and replace "idx" with "del" to delete instead) | ||||
| * note: paths starting with `/` are absolute URLs, but you can also do `../3.txt` relative to the destination folder of each uploaded file | ||||
| 
 | ||||
| ### hook import | ||||
| ## hook import | ||||
| 
 | ||||
| the `I` flag runs the hook inside copyparty,  which can be very useful and dangerous: | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue