File completion, where you want to present the output of readdir() with 
file-type hints, is a great place to use fstatat() and avoid string 
manipulation.

The first diff is for ftpd's NLIST support, which is used by ftp clients' 
filename completion.  Note this corrects(?) a quirk in NLIST's behavior: 
currently if you ask for the NLIST of a path beginning with "./", ftpd 
will silently suppress the "./" prefix on the returned paths:
        ftp> nlist ftp
        150 Opening ASCII mode data connection for 'file list'.
        ftp/rfc
        226 Transfer complete.
        ftp> nlist ./ftp
        150 Opening ASCII mode data connection for 'file list'.
        ftp/rfc
        226 Transfer complete.
        ftp>
I believe that was a side-effect of how argumentless NLIST was supported: 
with this diff it only suppresses the "./" prefix if you do NLIST or 
"NLIST ."  Or maybe send_file_list() should just take an argument that 
suppresses the prefix, for use by bare NLIST?

The second is in mg's filename completion on filesystems which don't store 
file-type info in the directory entry: ext2, cd9660, udf, and nfs.  
Tested against a udf filesystem.


Index: libexec/ftpd/ftpd.c
===================================================================
RCS file: /data/src/openbsd/src/libexec/ftpd/ftpd.c,v
retrieving revision 1.216
diff -u -p -r1.216 ftpd.c
--- libexec/ftpd/ftpd.c 4 May 2016 19:48:08 -0000       1.216
+++ libexec/ftpd/ftpd.c 28 Jun 2016 06:59:10 -0000
@@ -2635,6 +2635,7 @@ send_file_list(char *whichf)
        int simple = 0;
        volatile int freeglob = 0;
        glob_t gl;
+       size_t prefixlen;
 
        if (strpbrk(whichf, "~{[*?") != NULL) {
                memset(&gl, 0, sizeof(gl));
@@ -2694,9 +2695,11 @@ send_file_list(char *whichf)
                if ((dirp = opendir(dirname)) == NULL)
                        continue;
 
+               if (dirname[0] == '.' && dirname[1] == '\0')
+                       prefixlen = 0;
+               else
+                       prefixlen = strlen(dirname) + 1;
                while ((dir = readdir(dirp)) != NULL) {
-                       char nbuf[PATH_MAX];
-
                        if (recvurg) {
                                myoob();
                                recvurg = 0;
@@ -2710,14 +2713,12 @@ send_file_list(char *whichf)
                            dir->d_namlen == 2)
                                continue;
 
-                       snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname,
-                                dir->d_name);
-
                        /*
                         * We have to do a stat to insure it's
                         * not a directory or special file.
                         */
-                       if (simple || (stat(nbuf, &st) == 0 &&
+                       if (simple ||
+                           (fstatat(dirfd(dirp), dir->d_name, &st, 0) == 0 &&
                            S_ISREG(st.st_mode))) {
                                if (dout == NULL) {
                                        dout = dataconn("file list", (off_t)-1,
@@ -2726,13 +2727,14 @@ send_file_list(char *whichf)
                                                goto out;
                                        transflag++;
                                }
-                               if (nbuf[0] == '.' && nbuf[1] == '/')
-                                       fprintf(dout, "%s%s\n", &nbuf[2],
-                                               type == TYPE_A ? "\r" : "");
-                               else
-                                       fprintf(dout, "%s%s\n", nbuf,
-                                               type == TYPE_A ? "\r" : "");
-                               byte_count += strlen(nbuf) + 1;
+
+                               if (prefixlen) {
+                                       fprintf(dout, "%s/", dirname);
+                                       byte_count += prefixlen;
+                               }
+                               fprintf(dout, "%s%s\n", dir->d_name,
+                                   type == TYPE_A ? "\r" : "");
+                               byte_count += dir->d_namlen + 1;
                        }
                }
                (void) closedir(dirp);
Index: usr.bin/mg/fileio.c
===================================================================
RCS file: /data/src/openbsd/src/usr.bin/mg/fileio.c,v
retrieving revision 1.100
diff -u -p -r1.100 fileio.c
--- usr.bin/mg/fileio.c 26 Jan 2016 18:02:51 -0000      1.100
+++ usr.bin/mg/fileio.c 28 Jun 2016 07:15:01 -0000
@@ -526,14 +526,8 @@ make_file_list(char *buf)
                } else if (dent->d_type == DT_LNK ||
                            dent->d_type == DT_UNKNOWN) {
                        struct stat     statbuf;
-                       char            statname[NFILEN + 2];
 
-                       statbuf.st_mode = 0;
-                       ret = snprintf(statname, sizeof(statname), "%s/%s",
-                           dir, dent->d_name);
-                       if (ret < 0 || ret > sizeof(statname) - 1)
-                               continue;
-                       if (stat(statname, &statbuf) < 0)
+                       if (fstatat(dirfd(dirp), dent->d_name, &statbuf, 0) < 0)
                                continue;
                        if (S_ISDIR(statbuf.st_mode))
                                isdir = 1;

Reply via email to