Modify drop protocol to allow transmission of broken symlinks in the URI list

This commit is contained in:
Kovid Goyal
2026-04-23 12:36:22 +05:30
parent 3cf037d263
commit 4c6f7ff6b5
4 changed files with 36 additions and 25 deletions

View File

@@ -169,10 +169,14 @@ If the client requests an entry that is not a supported URI type the
terminal must reply with ``EUNKNOWN``.
Terminals must ONLY send data for regular files or directories. Symbolic links must be
resolved and the corresponding file or directory read. If the terminal does not have
permission to read the file it must reply with ``EPERM``. Terminals
must respond with ``EINVAL`` if the file is not a regular file after
resolving symlinks and ``ENOENT`` if the file does not exist. If an
resolved and the corresponding file or directory read. Only if the symbolic
link cannot be resolved must it be transmitted as a symbolic link (in which
case ``X=1`` and the payload is the base64 encoded target of the symlink. See
below for more details about sending symlinks.
If the terminal does not have permission to read the file it must reply with
``EPERM``. Terminals must respond with ``EINVAL`` if the file is not a regular
file after resolving symlinks and ``ENOENT`` if the file does not exist. If an
I/O error occurs the terminal must send ``EIO``.
For security reasons, terminals must reply with ``EPERM`` if the drag

View File

@@ -396,7 +396,10 @@ func (dnd *dnd) on_remote_drop_data(cmd DC) (err error) {
}
if e.dest == nil { // this entry is finished
drop_status.open_remote_dir.num_children_finished++
if e.item_type != 0 && e.item_type != 1 {
if len(e.children) > 0 {
if e.item_type != 0 && e.item_type != 1 {
dnd.lp.QueueDnDData(DC{Type: 'r', Yp: e.item_type}) // close directory in terminal
}
// TODO: request the children
}
}

View File

@@ -690,18 +690,7 @@ get_nth_file_url(const char *uri_list, size_t uri_list_sz, int n, char **path_ou
*query_or_fragment_start = 0;
url_decode_inplace(path);
if (path[0] != '/') { *error_out = "EINVAL"; return false; }
char resolved[PATH_MAX];
if (!realpath(path, resolved)) {
switch (errno) {
case ENOENT: case ENOTDIR: case ELOOP: *error_out = "ENOENT"; break;
case EACCES: case EPERM: *error_out = "EPERM"; break;
default: *error_out = "EINVAL"; break;
}
return false;
}
*path_out = strdup(resolved);
*path_out = strdup(path);
if (!*path_out) { *error_out = "ENOMEM"; return false; }
return true;
}
@@ -949,6 +938,16 @@ drop_send_dir_listing(Window *w, const char *path) {
queue_payload_to_child(w->id, w->drop.client_id, &w->drop.pending, hdr, hdr_sz, NULL, 0, true);
}
static void
drop_send_symlink(Window *w, const char *target, size_t sz) {
char hdr[128];
int hdr_sz = snprintf(hdr, sizeof(hdr), "\x1b]%d;t=r", DND_CODE);
hdr_sz += drop_append_request_keys(w, hdr + hdr_sz, sizeof(hdr) - hdr_sz);
hdr_sz += snprintf(hdr + hdr_sz, sizeof(hdr) - hdr_sz, ":X=1");
queue_payload_to_child(w->id, w->drop.client_id, &w->drop.pending, hdr, hdr_sz, target, sz, true);
queue_payload_to_child(w->id, w->drop.client_id, &w->drop.pending, hdr, hdr_sz, NULL, 0, true);
}
/* Send the file/directory at URI-list index idx.
* Returns true if completed synchronously, false if async file I/O started. */
static bool
@@ -970,7 +969,7 @@ do_drop_request_uri_data(Window *w, int32_t mime_idx, int32_t file_idx) {
if (file_idx < 1) { drop_send_error(w, EINVAL); return true; }
int file_n = file_idx - 1;
char *path = NULL;
RAII_ALLOC(char, path, NULL);
const char *err = NULL;
if (!get_nth_file_url(w->drop.uri_list, w->drop.uri_list_sz, file_n, &path, &err)) {
drop_send_error_str(w, err);
@@ -979,12 +978,18 @@ do_drop_request_uri_data(Window *w, int32_t mime_idx, int32_t file_idx) {
struct stat st;
if (stat(path, &st) < 0) {
free(path);
switch (errno) {
case ENOENT: case ENOTDIR: drop_send_error(w, ENOENT); break;
case EACCES: case EPERM: drop_send_error(w, EPERM); break;
default: drop_send_error(w, EIO); break;
if (lstat(path, &st) < 0) {
switch (errno) {
case ENOENT: case ENOTDIR: drop_send_error(w, ENOENT); break;
case EACCES: case EPERM: drop_send_error(w, EPERM); break;
default: drop_send_error(w, EIO); break;
}
return true;
}
// We have a broken symlink
char target[PATH_MAX]; ssize_t tgtsz;
if ((tgtsz = readlink(path, target, sizeof(target)-1)) < 0) drop_send_error(w, ENOENT);
drop_send_symlink(w, target, tgtsz);
return true;
}
@@ -998,7 +1003,6 @@ do_drop_request_uri_data(Window *w, int32_t mime_idx, int32_t file_idx) {
drop_send_error(w, EINVAL);
sync = true;
}
free(path);
return sync;
}

View File

@@ -42,7 +42,7 @@ def create_fs(base):
f.write(b'x' * sz)
os.makedirs(join('d1', 'sd', 'ssd'))
os.mkdir(join('d2'))
# os.symlink('/does-not-exist', join('s1'))
os.symlink('/does-not-exist', join('s1'))
os.symlink('d1', join('sd'))
os.symlink('/', join('sr'))
os.symlink('../d1', join('d1', 'sr'))