Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0
2 : /*
3 : * This module exports the functions:
4 : *
5 : * 'int set_selection_user(struct tiocl_selection __user *,
6 : * struct tty_struct *)'
7 : * 'int set_selection_kernel(struct tiocl_selection *, struct tty_struct *)'
8 : * 'void clear_selection(void)'
9 : * 'int paste_selection(struct tty_struct *)'
10 : * 'int sel_loadlut(u32 __user *)'
11 : *
12 : * Now that /dev/vcs exists, most of this can disappear again.
13 : */
14 :
15 : #include <linux/module.h>
16 : #include <linux/tty.h>
17 : #include <linux/sched.h>
18 : #include <linux/mm.h>
19 : #include <linux/mutex.h>
20 : #include <linux/slab.h>
21 : #include <linux/types.h>
22 :
23 : #include <linux/uaccess.h>
24 :
25 : #include <linux/kbd_kern.h>
26 : #include <linux/vt_kern.h>
27 : #include <linux/consolemap.h>
28 : #include <linux/selection.h>
29 : #include <linux/tiocl.h>
30 : #include <linux/console.h>
31 : #include <linux/tty_flip.h>
32 :
33 : #include <linux/sched/signal.h>
34 :
35 : /* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
36 : #define is_space_on_vt(c) ((c) == ' ')
37 :
38 : /* FIXME: all this needs locking */
39 : static struct vc_selection {
40 : struct mutex lock;
41 : struct vc_data *cons; /* must not be deallocated */
42 : char *buffer;
43 : unsigned int buf_len;
44 : volatile int start; /* cleared by clear_selection */
45 : int end;
46 : } vc_sel = {
47 : .lock = __MUTEX_INITIALIZER(vc_sel.lock),
48 : .start = -1,
49 : };
50 :
51 : /* clear_selection, highlight and highlight_pointer can be called
52 : from interrupt (via scrollback/front) */
53 :
54 : /* set reverse video on characters s-e of console with selection. */
55 0 : static inline void highlight(const int s, const int e)
56 : {
57 0 : invert_screen(vc_sel.cons, s, e-s+2, true);
58 0 : }
59 :
60 : /* use complementary color to show the pointer */
61 0 : static inline void highlight_pointer(const int where)
62 : {
63 0 : complement_pos(vc_sel.cons, where);
64 0 : }
65 :
66 : static u32
67 0 : sel_pos(int n, bool unicode)
68 : {
69 0 : if (unicode)
70 0 : return screen_glyph_unicode(vc_sel.cons, n / 2);
71 0 : return inverse_translate(vc_sel.cons, screen_glyph(vc_sel.cons, n),
72 : false);
73 0 : }
74 :
75 : /**
76 : * clear_selection - remove current selection
77 : *
78 : * Remove the current selection highlight, if any from the console holding the
79 : * selection.
80 : *
81 : * Locking: The caller must hold the console lock.
82 : */
83 0 : void clear_selection(void)
84 : {
85 0 : highlight_pointer(-1); /* hide the pointer */
86 0 : if (vc_sel.start != -1) {
87 0 : highlight(vc_sel.start, vc_sel.end);
88 0 : vc_sel.start = -1;
89 0 : }
90 0 : }
91 : EXPORT_SYMBOL_GPL(clear_selection);
92 :
93 0 : bool vc_is_sel(const struct vc_data *vc)
94 : {
95 0 : return vc == vc_sel.cons;
96 : }
97 :
98 : /*
99 : * User settable table: what characters are to be considered alphabetic?
100 : * 128 bits. Locked by the console lock.
101 : */
102 : static u32 inwordLut[]={
103 : 0x00000000, /* control chars */
104 : 0x03FFE000, /* digits and "-./" */
105 : 0x87FFFFFE, /* uppercase and '_' */
106 : 0x07FFFFFE, /* lowercase */
107 : };
108 :
109 0 : static inline int inword(const u32 c)
110 : {
111 0 : return c > 0x7f || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1);
112 : }
113 :
114 : /**
115 : * sel_loadlut() - load the LUT table
116 : * @lut: user table
117 : *
118 : * Load the LUT table from user space. Make a temporary copy so a partial
119 : * update doesn't make a mess.
120 : *
121 : * Locking: The console lock is acquired.
122 : */
123 0 : int sel_loadlut(u32 __user *lut)
124 : {
125 0 : u32 tmplut[ARRAY_SIZE(inwordLut)];
126 :
127 0 : if (copy_from_user(tmplut, lut, sizeof(inwordLut)))
128 0 : return -EFAULT;
129 :
130 0 : console_lock();
131 0 : memcpy(inwordLut, tmplut, sizeof(inwordLut));
132 0 : console_unlock();
133 :
134 0 : return 0;
135 0 : }
136 :
137 : /* does screen address p correspond to character at LH/RH edge of screen? */
138 0 : static inline int atedge(const int p, int size_row)
139 : {
140 0 : return (!(p % size_row) || !((p + 2) % size_row));
141 : }
142 :
143 : /* stores the char in UTF8 and returns the number of bytes used (1-4) */
144 0 : static int store_utf8(u32 c, char *p)
145 : {
146 0 : if (c < 0x80) {
147 : /* 0******* */
148 0 : p[0] = c;
149 0 : return 1;
150 0 : } else if (c < 0x800) {
151 : /* 110***** 10****** */
152 0 : p[0] = 0xc0 | (c >> 6);
153 0 : p[1] = 0x80 | (c & 0x3f);
154 0 : return 2;
155 0 : } else if (c < 0x10000) {
156 : /* 1110**** 10****** 10****** */
157 0 : p[0] = 0xe0 | (c >> 12);
158 0 : p[1] = 0x80 | ((c >> 6) & 0x3f);
159 0 : p[2] = 0x80 | (c & 0x3f);
160 0 : return 3;
161 0 : } else if (c < 0x110000) {
162 : /* 11110*** 10****** 10****** 10****** */
163 0 : p[0] = 0xf0 | (c >> 18);
164 0 : p[1] = 0x80 | ((c >> 12) & 0x3f);
165 0 : p[2] = 0x80 | ((c >> 6) & 0x3f);
166 0 : p[3] = 0x80 | (c & 0x3f);
167 0 : return 4;
168 : } else {
169 : /* outside Unicode, replace with U+FFFD */
170 0 : p[0] = 0xef;
171 0 : p[1] = 0xbf;
172 0 : p[2] = 0xbd;
173 0 : return 3;
174 : }
175 0 : }
176 :
177 : /**
178 : * set_selection_user - set the current selection.
179 : * @sel: user selection info
180 : * @tty: the console tty
181 : *
182 : * Invoked by the ioctl handle for the vt layer.
183 : *
184 : * Locking: The entire selection process is managed under the console_lock.
185 : * It's a lot under the lock but its hardly a performance path.
186 : */
187 0 : int set_selection_user(const struct tiocl_selection __user *sel,
188 : struct tty_struct *tty)
189 : {
190 0 : struct tiocl_selection v;
191 :
192 0 : if (copy_from_user(&v, sel, sizeof(*sel)))
193 0 : return -EFAULT;
194 :
195 : /*
196 : * TIOCL_SELCLEAR and TIOCL_SELPOINTER are OK to use without
197 : * CAP_SYS_ADMIN as they do not modify the selection.
198 : */
199 0 : switch (v.sel_mode) {
200 : case TIOCL_SELCLEAR:
201 : case TIOCL_SELPOINTER:
202 0 : break;
203 : default:
204 0 : if (!capable(CAP_SYS_ADMIN))
205 0 : return -EPERM;
206 0 : }
207 :
208 0 : return set_selection_kernel(&v, tty);
209 0 : }
210 :
211 0 : static int vc_selection_store_chars(struct vc_data *vc, bool unicode)
212 : {
213 0 : char *bp, *obp;
214 0 : unsigned int i;
215 :
216 : /* Allocate a new buffer before freeing the old one ... */
217 : /* chars can take up to 4 bytes with unicode */
218 0 : bp = kmalloc_array((vc_sel.end - vc_sel.start) / 2 + 1, unicode ? 4 : 1,
219 : GFP_KERNEL | __GFP_NOWARN);
220 0 : if (!bp) {
221 0 : printk(KERN_WARNING "selection: kmalloc() failed\n");
222 0 : clear_selection();
223 0 : return -ENOMEM;
224 : }
225 0 : kfree(vc_sel.buffer);
226 0 : vc_sel.buffer = bp;
227 :
228 0 : obp = bp;
229 0 : for (i = vc_sel.start; i <= vc_sel.end; i += 2) {
230 0 : u32 c = sel_pos(i, unicode);
231 0 : if (unicode)
232 0 : bp += store_utf8(c, bp);
233 : else
234 0 : *bp++ = c;
235 0 : if (!is_space_on_vt(c))
236 0 : obp = bp;
237 0 : if (!((i + 2) % vc->vc_size_row)) {
238 : /* strip trailing blanks from line and add newline,
239 : unless non-space at end of line. */
240 0 : if (obp != bp) {
241 0 : bp = obp;
242 0 : *bp++ = '\r';
243 0 : }
244 0 : obp = bp;
245 0 : }
246 0 : }
247 0 : vc_sel.buf_len = bp - vc_sel.buffer;
248 :
249 0 : return 0;
250 0 : }
251 :
252 0 : static int vc_do_selection(struct vc_data *vc, unsigned short mode, int ps,
253 : int pe)
254 : {
255 0 : int new_sel_start, new_sel_end, spc;
256 0 : bool unicode = vt_do_kdgkbmode(fg_console) == K_UNICODE;
257 :
258 0 : switch (mode) {
259 : case TIOCL_SELCHAR: /* character-by-character selection */
260 0 : new_sel_start = ps;
261 0 : new_sel_end = pe;
262 0 : break;
263 : case TIOCL_SELWORD: /* word-by-word selection */
264 0 : spc = is_space_on_vt(sel_pos(ps, unicode));
265 0 : for (new_sel_start = ps; ; ps -= 2) {
266 0 : if ((spc && !is_space_on_vt(sel_pos(ps, unicode))) ||
267 0 : (!spc && !inword(sel_pos(ps, unicode))))
268 0 : break;
269 0 : new_sel_start = ps;
270 0 : if (!(ps % vc->vc_size_row))
271 0 : break;
272 0 : }
273 :
274 0 : spc = is_space_on_vt(sel_pos(pe, unicode));
275 0 : for (new_sel_end = pe; ; pe += 2) {
276 0 : if ((spc && !is_space_on_vt(sel_pos(pe, unicode))) ||
277 0 : (!spc && !inword(sel_pos(pe, unicode))))
278 0 : break;
279 0 : new_sel_end = pe;
280 0 : if (!((pe + 2) % vc->vc_size_row))
281 0 : break;
282 0 : }
283 0 : break;
284 : case TIOCL_SELLINE: /* line-by-line selection */
285 0 : new_sel_start = rounddown(ps, vc->vc_size_row);
286 0 : new_sel_end = rounddown(pe, vc->vc_size_row) +
287 0 : vc->vc_size_row - 2;
288 0 : break;
289 : case TIOCL_SELPOINTER:
290 0 : highlight_pointer(pe);
291 0 : return 0;
292 : default:
293 0 : return -EINVAL;
294 : }
295 :
296 : /* remove the pointer */
297 0 : highlight_pointer(-1);
298 :
299 : /* select to end of line if on trailing space */
300 0 : if (new_sel_end > new_sel_start &&
301 0 : !atedge(new_sel_end, vc->vc_size_row) &&
302 0 : is_space_on_vt(sel_pos(new_sel_end, unicode))) {
303 0 : for (pe = new_sel_end + 2; ; pe += 2)
304 0 : if (!is_space_on_vt(sel_pos(pe, unicode)) ||
305 0 : atedge(pe, vc->vc_size_row))
306 0 : break;
307 0 : if (is_space_on_vt(sel_pos(pe, unicode)))
308 0 : new_sel_end = pe;
309 0 : }
310 0 : if (vc_sel.start == -1) /* no current selection */
311 0 : highlight(new_sel_start, new_sel_end);
312 0 : else if (new_sel_start == vc_sel.start)
313 : {
314 0 : if (new_sel_end == vc_sel.end) /* no action required */
315 0 : return 0;
316 0 : else if (new_sel_end > vc_sel.end) /* extend to right */
317 0 : highlight(vc_sel.end + 2, new_sel_end);
318 : else /* contract from right */
319 0 : highlight(new_sel_end + 2, vc_sel.end);
320 0 : }
321 0 : else if (new_sel_end == vc_sel.end)
322 : {
323 0 : if (new_sel_start < vc_sel.start) /* extend to left */
324 0 : highlight(new_sel_start, vc_sel.start - 2);
325 : else /* contract from left */
326 0 : highlight(vc_sel.start, new_sel_start - 2);
327 0 : }
328 : else /* some other case; start selection from scratch */
329 : {
330 0 : clear_selection();
331 0 : highlight(new_sel_start, new_sel_end);
332 : }
333 0 : vc_sel.start = new_sel_start;
334 0 : vc_sel.end = new_sel_end;
335 :
336 0 : return vc_selection_store_chars(vc, unicode);
337 0 : }
338 :
339 0 : static int vc_selection(struct vc_data *vc, struct tiocl_selection *v,
340 : struct tty_struct *tty)
341 : {
342 0 : int ps, pe;
343 :
344 0 : poke_blanked_console();
345 :
346 0 : if (v->sel_mode == TIOCL_SELCLEAR) {
347 : /* useful for screendump without selection highlights */
348 0 : clear_selection();
349 0 : return 0;
350 : }
351 :
352 0 : v->xs = min_t(u16, v->xs - 1, vc->vc_cols - 1);
353 0 : v->ys = min_t(u16, v->ys - 1, vc->vc_rows - 1);
354 0 : v->xe = min_t(u16, v->xe - 1, vc->vc_cols - 1);
355 0 : v->ye = min_t(u16, v->ye - 1, vc->vc_rows - 1);
356 :
357 0 : if (mouse_reporting() && (v->sel_mode & TIOCL_SELMOUSEREPORT)) {
358 0 : mouse_report(tty, v->sel_mode & TIOCL_SELBUTTONMASK, v->xs,
359 0 : v->ys);
360 0 : return 0;
361 : }
362 :
363 0 : ps = v->ys * vc->vc_size_row + (v->xs << 1);
364 0 : pe = v->ye * vc->vc_size_row + (v->xe << 1);
365 0 : if (ps > pe) /* make vc_sel.start <= vc_sel.end */
366 0 : swap(ps, pe);
367 :
368 0 : if (vc_sel.cons != vc) {
369 0 : clear_selection();
370 0 : vc_sel.cons = vc;
371 0 : }
372 :
373 0 : return vc_do_selection(vc, v->sel_mode, ps, pe);
374 0 : }
375 :
376 0 : int set_selection_kernel(struct tiocl_selection *v, struct tty_struct *tty)
377 : {
378 0 : int ret;
379 :
380 0 : mutex_lock(&vc_sel.lock);
381 0 : console_lock();
382 0 : ret = vc_selection(vc_cons[fg_console].d, v, tty);
383 0 : console_unlock();
384 0 : mutex_unlock(&vc_sel.lock);
385 :
386 0 : return ret;
387 0 : }
388 : EXPORT_SYMBOL_GPL(set_selection_kernel);
389 :
390 : /* Insert the contents of the selection buffer into the
391 : * queue of the tty associated with the current console.
392 : * Invoked by ioctl().
393 : *
394 : * Locking: called without locks. Calls the ldisc wrongly with
395 : * unsafe methods,
396 : */
397 0 : int paste_selection(struct tty_struct *tty)
398 : {
399 0 : struct vc_data *vc = tty->driver_data;
400 0 : int pasted = 0;
401 0 : size_t count;
402 0 : struct tty_ldisc *ld;
403 0 : DECLARE_WAITQUEUE(wait, current);
404 0 : int ret = 0;
405 :
406 0 : bool bp = vc->vc_bracketed_paste;
407 : static const char bracketed_paste_start[] = "\033[200~";
408 : static const char bracketed_paste_end[] = "\033[201~";
409 0 : const char *bps = bp ? bracketed_paste_start : NULL;
410 0 : const char *bpe = bp ? bracketed_paste_end : NULL;
411 :
412 0 : console_lock();
413 0 : poke_blanked_console();
414 0 : console_unlock();
415 :
416 0 : ld = tty_ldisc_ref_wait(tty);
417 0 : if (!ld)
418 0 : return -EIO; /* ldisc was hung up */
419 0 : tty_buffer_lock_exclusive(&vc->port);
420 :
421 0 : add_wait_queue(&vc->paste_wait, &wait);
422 0 : mutex_lock(&vc_sel.lock);
423 0 : while (vc_sel.buffer && (vc_sel.buf_len > pasted || bpe)) {
424 0 : set_current_state(TASK_INTERRUPTIBLE);
425 0 : if (signal_pending(current)) {
426 0 : ret = -EINTR;
427 0 : break;
428 : }
429 0 : if (tty_throttled(tty)) {
430 0 : mutex_unlock(&vc_sel.lock);
431 0 : schedule();
432 0 : mutex_lock(&vc_sel.lock);
433 0 : continue;
434 : }
435 0 : __set_current_state(TASK_RUNNING);
436 :
437 0 : if (bps) {
438 0 : bps += tty_ldisc_receive_buf(ld, bps, NULL, strlen(bps));
439 0 : if (*bps != '\0')
440 0 : continue;
441 0 : bps = NULL;
442 0 : }
443 :
444 0 : count = vc_sel.buf_len - pasted;
445 0 : if (count) {
446 0 : pasted += tty_ldisc_receive_buf(ld, vc_sel.buffer + pasted,
447 0 : NULL, count);
448 0 : if (vc_sel.buf_len > pasted)
449 0 : continue;
450 0 : }
451 :
452 0 : if (bpe) {
453 0 : bpe += tty_ldisc_receive_buf(ld, bpe, NULL, strlen(bpe));
454 0 : if (*bpe == '\0')
455 0 : bpe = NULL;
456 0 : }
457 : }
458 0 : mutex_unlock(&vc_sel.lock);
459 0 : remove_wait_queue(&vc->paste_wait, &wait);
460 0 : __set_current_state(TASK_RUNNING);
461 :
462 0 : tty_buffer_unlock_exclusive(&vc->port);
463 0 : tty_ldisc_deref(ld);
464 0 : return ret;
465 0 : }
466 : EXPORT_SYMBOL_GPL(paste_selection);
|