{"id":1881,"date":"2014-12-18T15:31:19","date_gmt":"2014-12-18T20:31:19","guid":{"rendered":"http:\/\/www.kickflop.net\/blog\/?p=1881"},"modified":"2014-12-18T15:31:19","modified_gmt":"2014-12-18T20:31:19","slug":"64-bits-is-too-many-bits","status":"publish","type":"post","link":"https:\/\/www.kickflop.net\/blog\/2014\/12\/18\/64-bits-is-too-many-bits\/","title":{"rendered":"64 bits is Too Many Bits"},"content":{"rendered":"<p>A seemingly simple move of NFS-exported home directories resulted in Apache suEXEC freaking out when referencing the new space. Here are the details and solution, because sometimes a debugging story is fun.<br \/>\n<!--more--><\/p>\n<h2>Friday<\/h2>\n<p><em>Setting: R&#038;D environment of ~800 physical and virtual hosts running Windows, Linux, Solaris and using DAS, NAS, and SAN with NFS and OpenAFS. Our direct customers are scientists, researchers, and software and hardware engineers.<\/em><\/p>\n<p>A small portion of our users, say 20%, have NFS-based home directories. Over the last ~18 years these have been served from varied hosts running SunOS 4.1 &#8211; 5.10. As part of our department&#8217;s ongoing weening from Sun\/Oracle hardware, it was recently finally time to get this NFS data moved from the host (Sun-Fire V240). Our Isilon IQ 7200x 3-node cluster, which I&#8217;d personally had only limited interaction with (and no training with) to date, seemed the appropriate destination for the data.<\/p>\n<p>After moving the data (twice[<a href=\"#fn1\">1<\/a>]), changing automounter maps in LDAP, and successfully performing basic usability testing, I called it a night and ended the Planned Outage event. Because what better time to make changes than Friday at 10PM, right?<\/p>\n<h2>Monday<\/h2>\n<p>Jimbo: &#8220;Our project&#8217;s CGI scripts in Perl at http:\/\/lab.our.org\/~jimbo\/cgi-bin, that have been working fine for 8 years, are all giving 500 Internal Server errors. We use these scripts as part of a high-value production process.&#8221;<\/p>\n<p><em>Let&#8217;s ignore for now everything wrong with the sentence above. I know.<\/em><\/p>\n<p>I SSHed into <code>lab.mitre.org<\/code> and had a look through recent data in <code>\/var\/log\/httpd\/lab-error_log<\/code>. It showed nothing interesting.<\/p>\n<p>Then I noticed that <code>suexec_log<\/code> had been written to recently. Having no idea what suexec_log even was, I looked through it to find that it&#8217;s the log for Apache httpd&#8217;s <a href=\"http:\/\/httpd.apache.org\/docs\/2.2\/suexec.html\">suEXEC<\/a> mechanism. While I&#8217;m not entirely Apache httpd clueless, I don&#8217;t exactly deal with every nook of it on even a yearly basis. I read up on suEXEC&#8217;s role and functionality then returned to <code>suexec_log<\/code>:<\/p>\n<pre>\r\ncannot stat directory: (\/home\/jimbo\/public_html\/cgi-bin)\r\n<\/pre>\n<p>Okay, that explains the 500 HTTP errors. However:<\/p>\n<pre>\r\nlab.our.org# cd \/home\/jimbo\/public_html\/cgi-bin\r\nlab.our.org# pwd\r\n\/home\/jimbo\/public_html\/cgi-bin\r\nlab.our.org#\r\n<\/pre>\n<p>From here, scratching my head for 30 minutes or so, I mucked around in the Isilon OneFS ACL for <code>\/home\/jimbo<\/code>, somewhat convinced I&#8217;d hit an ACL peculiarity like the previous <a href=\"#fn1\">one<\/a>. That led nowhere, so it was time to examine just what the heck Apache httpd and its <code>suexec<\/code> helper were doing under the covers. Firing up <code>strace -fq ... <\/code> on the httpd PIDs while hitting one of Jimbo&#8217;s troubled CGI scripts, I saw this:<\/p>\n<pre>\r\n...\r\nsetgroups32(2, [1130, 67])  = 0\r\nsetuid32(21389)             = 0\r\ngetcwd(\"\/home\/jimbo\/public_html\/cgi-bin\"..., 4096) = 32\r\nchdir(\"\/home\/jimbo\")        = 0\r\nchdir(\"public_html\")        = 0\r\ngetcwd(\"\/home\/jimbo\/public_html\"..., 4096) = 24\r\nchdir(\"\/home\/jimbo\/public_html\/cgi-bin\") = 0\r\nlstat64(\"\/home\/jimbo\/public_html\/cgi-bin\", {st_mode=S_IFDIR|0755, st_size=1955, ...}) = 0\r\nwrite(2, \"suexec policy violation: see sue\"..., 57) = 57\r\n<\/pre>\n<p>According to the <code>lstat()<\/code> man page, a return code of 0 is success. The line following the <code>lstat()<\/code> call is writing to stderr that there was a suexec problem and to see our friend <code>suexec_log<\/code> for information. We already know how that goes. For some reason, <code>lstat()<\/code> was returning what we understand to be success and there was no further system call taking place before the code was logging an error line. Funky.<\/p>\n<p>Pouring over the <a href=\"https:\/\/github.com\/apache\/httpd\/blob\/2.2.x\/support\/suexec.c\">suexec.c<\/a> code, this is the block delivering our line to <code>suexec_log<\/code>:<\/p>\n<pre>\r\nif (((lstat(cwd, &dir_info)) != 0) || !(S_ISDIR(dir_info.st_mode))) {\r\n    log_err(\"cannot stat directory: (%s)\\n\", cwd);\r\n    exit(115);\r\n}\r\n<\/pre>\n<p>So, given this lazy code, it&#8217;s possible the <code>lstat<\/code> part of it was working fine but the POSIX <code>S_ISDIR<\/code> macro call was returning a false value. Note, too, that our strace showed <code>lstat64()<\/code> being called, which means <em>in theory<\/em>, &#8220;largefile&#8221; support is compiled in and enabled. Anyway, I messed around with a custom one-off version of the code, running it via the command-line for a bit, before I decided it was easier to just strip things down to the basics in my own new code.<\/p>\n<p>Using <code>lstat()<\/code> and actually reporting what <code>errno<\/code> got set to on lstat failure lead me in the final right direction. On <code>lab.our.org<\/code>, which happens to be an old 32-bit CentOS 5.11 host, my code reported this:<\/p>\n<pre>\r\nPath:                   \/home\/jimbo\/public_html\/cgi-bin\r\nFile Size:              0 bytes\r\nNumber of Links:        0\r\nFile inode:             1580990986\r\nFile Permissions:       ----------\r\nERROR: lstat errno says \"Value too large for defined data type\"\r\n<\/pre>\n<p>As soon as I saw that, I ran the same code on one of our 64-bit hosts:<\/p>\n<pre>\r\nPath:                   \/home\/jimbo\/public_html\/cgi-bin\r\nFile Size:              1982 bytes\r\nNumber of Links:        4\r\nFile inode:             1580990986\r\nFile Permissions:       drwxr-xr-x\r\nSuccess - Exiting 0\r\n<\/pre>\n<p>Searching for &#8220;Value too large for defined data type&#8221; in the EMC\/Isilon support portal showed me that others had hit this as well, one even mentioning Apache httpd.<\/p>\n<p>Turns out Isilon OneFS serves up 64-bit file IDs by default. Unless 32-bit applications allow for compilation with &#8220;largefile&#8221; support, and are compiled with it, they don&#8217;t know how to properly handle <code>stat()\/lstat()<\/code> calls against 64-bit file-id-having files.<\/p>\n<p>I hear you saying, &#8220;Right, but, your strace shows that the code was calling <code>lstat64()<\/code> which is supposed to handle things fine.&#8221;<\/p>\n<p>I know, right? But, based on the instructions in the EMC\/Isilon support portal, I forced 32-bit file IDs for the NFS export in question and everything immediately began functioning.<\/p>\n<pre>\r\nisibed-1# isi nfs exports modify 5 --return-32bit-file-ids=yes\r\n<\/pre>\n<p>Clearly something else is at play here. The lstat64() was in fact returning success (0) all along, but the fact remains that forcing 32-bit file IDs for the NFS export solved the problem. Maybe something in the POSIX <code>S_ISDIR<\/code> macro was failing, which doesn&#8217;t show in the strace output.<\/p>\n<p>You could even go so far as to say that my one-off non-largefile-enabled code that showed the &#8220;Value too large&#8230;&#8221; error and led me to the EMC\/Isilon support thread was an fortuitous goof that gave me a working solution. And I don&#8217;t 100% understand <em>how<\/em> it solved the issue.<\/p>\n<h2>Footnotes<\/h2>\n<ol>\n<li><a name='fn1'><\/a>Prior to this debacle described in this main blog post, I&#8217;d used <code>rsync -av \/old\/ \/new<\/code> to perform the NFS home directory migration the week before. Due to embarassingly poor testing on my part, I did not notice until the next morning that writes (and only writes) to user home directories by the users were not working. Examining the problem showed that each user did not even own his\/her home directory. After an hour of digging around in various strace sessions, it became evident that only a data copy command issuing an actual <code>chown(2)<\/code> call resulted in the proper ownership of the destination data. Neither <code>rsync<\/code> or <code>tar<\/code> do this in the way OneFS wanted, but <code>cp -rpP<\/code> does.\n<pre>\r\n# cp -rpP\r\nchown(\"\/newusers\/testjblaine\/sdfdsa\", 26560, 0) = 0\r\nchown(\"\/newusers\/testjblaine\", 26560, 0) = 0\r\n\r\n# rsync\r\nlchown(\"testjblaine\", 26560, 0) = 0\r\nlchown(\"testjblaine\/.sdfdsa.2Ba4BO\", 26560, 0) = 0\r\n\r\n# tar\r\nfchownat(4, \"testjblaine\", 0x000067C0, 0x00000000, 0x00000000) = 0\r\nfchownat(4, \"sdfdsa\", 0x000067C0, 0x00000000, 0x00000000) = 0\r\n<\/pre>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>A seemingly simple move of NFS-exported home directories resulted in Apache suEXEC freaking out when referencing the new space. Here&hellip;<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[51,35,11,48],"tags":[],"class_list":["post-1881","post","type-post","status-publish","format-standard","hentry","category-devops","category-linux","category-sysadmin","category-unixlinux"],"_links":{"self":[{"href":"https:\/\/www.kickflop.net\/blog\/wp-json\/wp\/v2\/posts\/1881","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kickflop.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kickflop.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kickflop.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kickflop.net\/blog\/wp-json\/wp\/v2\/comments?post=1881"}],"version-history":[{"count":10,"href":"https:\/\/www.kickflop.net\/blog\/wp-json\/wp\/v2\/posts\/1881\/revisions"}],"predecessor-version":[{"id":1897,"href":"https:\/\/www.kickflop.net\/blog\/wp-json\/wp\/v2\/posts\/1881\/revisions\/1897"}],"wp:attachment":[{"href":"https:\/\/www.kickflop.net\/blog\/wp-json\/wp\/v2\/media?parent=1881"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kickflop.net\/blog\/wp-json\/wp\/v2\/categories?post=1881"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kickflop.net\/blog\/wp-json\/wp\/v2\/tags?post=1881"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}