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 | * [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 | * [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) |   * 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 | # 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 | * [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 | * [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 | * [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... |     # "ka" is a dictionary with info from copyparty... | ||||||
| 
 | 
 | ||||||
|     # but because we are running inside copyparty, we don't need such courtesies; |     # 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 |     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" |     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]) |     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: |     # must return a dictionary with: | ||||||
|     #  "rc": the retcode; 0 is ok |     #  "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( | def _parsehook( | ||||||
|     log: Optional["NamedLogger"], cmd: str |     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 = "" |     areq = "" | ||||||
|     chk = False |     chk = False | ||||||
|     fork = False |     fork = False | ||||||
|  | @ -3906,6 +3906,7 @@ def _runhook( | ||||||
|             "txt": txt, |             "txt": txt, | ||||||
|         } |         } | ||||||
|         if imp: |         if imp: | ||||||
|  |             ja["log"] = log | ||||||
|             mod = loadpy(acmd[0], False) |             mod = loadpy(acmd[0], False) | ||||||
|             return mod.main(ja) |             return mod.main(ja) | ||||||
|         arg = json.dumps(ja) |         arg = json.dumps(ja) | ||||||
|  | @ -4003,7 +4004,7 @@ def runhook( | ||||||
|                 else: |                 else: | ||||||
|                     ret[k] = v |                     ret[k] = v | ||||||
|         except Exception as ex: |         except Exception as ex: | ||||||
|             (log or print)("hook: {}".format(ex)) |             (log or print)("hook: %r, %s" % (ex, ex)) | ||||||
|             if ",c," in "," + cmd: |             if ",c," in "," + cmd: | ||||||
|                 return {} |                 return {} | ||||||
|             break |             break | ||||||
|  |  | ||||||
|  | @ -15,6 +15,7 @@ | ||||||
|     * [general](#general) |     * [general](#general) | ||||||
| * [event hooks](#event-hooks) - on writing your own [hooks](../README.md#event-hooks) | * [event hooks](#event-hooks) - on writing your own [hooks](../README.md#event-hooks) | ||||||
|     * [hook effects](#hook-effects) - hooks can cause intentional side-effects |     * [hook effects](#hook-effects) - hooks can cause intentional side-effects | ||||||
|  |     * [hook import](#hook-import) - the `I` flag runs the hook inside copyparty | ||||||
| * [assumptions](#assumptions) | * [assumptions](#assumptions) | ||||||
|     * [mdns](#mdns) |     * [mdns](#mdns) | ||||||
| * [sfx repack](#sfx-repack) - reduce the size of an sfx by removing features | * [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) | 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 | * 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: | the `I` flag runs the hook inside copyparty,  which can be very useful and dangerous: | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue